Michele Nasti
Full stack developer with an eye for the web
2024-03-03T23:00:00Z
https://michelenasti.com/
Michele Nasti
michele@nasti.dev
What’s new in Javascript?
2015-01-07T00:00:00Z
https://michelenasti.com/2015/01/whats-new-in-javascript/
<p>Javascript esiste praticamente da sempre, per me che ho conosciuto i computer quando Netscape Navigator era alla versione 3. E da sempre ce l’hanno raccontato male: ci hanno parlato delle sue limitazioni, del linguaggio che non si capisce, del codice interpretato, la sicurezza, i cookies etc…</p>
<p>Poi è arrivato Gmail, e qualuno ha iniziato a capire che forse con Javascript si poteva fare tantissimo anche lato client.</p>
<p>Io, quando ho iniziato la mia avventura informatica, ho apprezzato da subito Java e gli oggetti e ho pure un po’ detestato la programazione web (nessun browser interpretava lo standard correttamente, ossia ognuno diceva di interpretarlo a modo suo). Era il 2004-2005. A distanza di anni ho provato un centinaio di altri linguaggi (specialmente quelli funzionali) e, ad ogni modo, voglio uscire dal mondo Java perchè <em>secondo me</em> non è il linguaggio del futuro. (Non lo è neanche javascript, ma il paradigma funzionale secondo me si!)</p>
<p>Così arrivo a javascript con <em>soli dieci</em> anni di ritardo. Una cosa mi consola: in questo mondo java-based sono davvero in pochi a sapere cosa javascript può fare, e come. Gli snippet di codice più interessanti li condividerò e commenterò con voi.</p>
<p>Per concludere: what’s new in Javascript? Se venite da Java, e il 99% di voi è come me, everything is brand new. La filosofia è diversa, lo scopo è diverso, le best practices e gli strumenti di lavoro sono diversi. E questo è il posto giusto per noi!</p>
Un breve riassunto sulla storia di Javascript
2015-01-07T23:01:17Z
https://michelenasti.com/2015/01/un-breve-riassunto-sulla-storia-di-javascript/
<p>Javascript è un linguaggio ideato da <a href="http://en.wikipedia.org/wiki/Brendan_Eich" title="Brendan Eich">Brendan Eich</a>, che all’epoca lavorava in Netscape. Il principale utilizzo di Javascript doveva essere quello di evitare rimbalzi di richeste / risposte da client a server solo per delle banali validazioni nei form. All’epoca, nel 1995, caricare una pagina poteva costare tantissimo e vedersela ritornare indietro con la scritta “devi accettare questa clausola per continuare” poteva essere moooolto costoso.</p>
<p>All’epoca di Javascript, la connessione più diffusa nel mondo era a 28.8kbps . Diciamo che adesso navighiamo a 1Mbit con le ADSL più sfigate, il ché vuol dire almeno 40 volte più veloce di allora.</p>
<p>Il primo nome di Javascript doveva essere Mocha, poi cambiato in LiveScript. Per accordi commerciali con la Sun, che spingeva per diffondere il linguaggio Java, si arrivò a Javascript, con cui non condivide praticamente nulla.</p>
<p>All’epoca in cui Netscape propose Javascript non vi erano altri browser concorrenti. Chi voleva andare sul web doveva per forza procurarsi (da floppy!) una copia di Netscape Navigator. Fu in questo frangente che Microsoft decise di sviluppare il primo browser, Internet Explorer, e per non farlo sentire inferiore al rivale lo fece iniziare direttamente dalla versione 3.</p>
<p>Tuttavia Microsoft implementò lo standard a modo proprio, e anzi uno standard non c’era neanche, infatti Microsoft per non incorrere in problemi legali chiamò il linguaggio JScript. Per gli sviluppatori ciò voleva dire solo una cosa: dovevano scrivere e testare due codici probabilmente incompatibili, uno per IE, uno per NN.</p>
<h2>EcmaScript</h2>
<p>Nel 1997 era chiaro a tutti che bisognava standardizzare il linguaggio per evitare di impazzire. Fu proposta una bozza alla European Computer Manufacturers Association, che se ne uscì con Ecma-262.</p>
<p>Quello che pochi sanno è che EcmaScript e Javascript non sono <em>proprio</em> la stessa cosa: Javascript è molto di più. Un’implementazione Javascript è composta dal <strong>Core (EcmaScript),</strong> un <strong>Document Object Model (DOM)</strong> e un <strong>Browser Object Model (BOM).</strong></p>
<p>EcmaScript infatti non è legato ai Browser. EcmaScript definisce solo la <strong>sintassi del linguaggio</strong> ed infatti non è specificato nessun metodo per l’input o per l’output del linguaggio. I Browser sono solo un ambiente su cui il linguaggio gira, ma altri sistemi possono usare EcmaScript e tararlo sulle loro necessità, come Adobe Flash e il suo ActionScript, o più recentemente NodeJS.</p>
<p>Un cenno sulle versioni di EcmaScript bisogna farlo. La <strong>prima versione</strong> era la stessa del Netscape 1.1 con l’unico cambiamento del supporto a Unicode. Già Javascript 1.2 che Netscape effettivamente distribuì non era conforme a questa prima edizione.</p>
<p>La <strong>seconda versione</strong> fu solo un aggiustamento editoriale, curando omissioni, aggiunte, cambiamenti.</p>
<p>La <strong>terza edizione</strong> di EcmaScript è stata la prima edizione ad aggiornare lo standard. Con questa versione abbiamo Javascript così come lo conosciamo oggi, quindi stringhe, errori, output numerici, espressioni regolari, try-catch, e così via. Questa versione è considerata come quella che ha fatto fare a Javascript il salto di qualità come linguaggio.</p>
<p>La <strong>quarta edizione</strong> è un cambiamento radicale rispetto alla terza: per citarne alcuni, abbiamo variabili fortemente tipizzate, nuove strutture dati, classi vere e ereditarietà. Per molti era un salto generazionale troppo grande e in effetti un <em>branch</em> della terza edizione, chiamato EcmaScript 3.1, fu sviluppato introducendo solo alcune piccole cose. La quarta edizione fu presto abbandonata dopo essere stata rilasciata, in favore della 3.1.</p>
<p>Per concludere, la <strong>quinta edizione</strong> dello standard è stata pubblicata nel 2009 e chiarisce una serie di ambiguità della versione 3 oltre ad aggiungere funzionalità, tra cui JSON, strict mode, e tanto altro che ha effetti su come EcmaScript interpreta ed esegue il codice.</p>
<h2>Come i browser hanno implementato gli standard</h2>
<p>Come già scritto, Javascript 1.1 ebbe una tale popolarità che fu subito richiesto di standardizzarlo in EcmaScript, tuttavia i processi di standardizzazione richiedono tempo e la versione di Javascript 1.2, uscita con Netscape Navigator 4, non era compatibile neanche con lo standard da loro proposto. Internet Explorer invece aveva implementato in un altro modo una serie di feature mal-specificate che di fatto rendevano Jscript un linguaggio diverso da Javascript 1.1.</p>
<p>Insomma, per un po’ si andò avanti dicendo sia in Netscape che in Microsoft che i loro browser implementavano EcmaScript, <em>peccato</em> che questo non era ancora uscito.</p>
<p>Soltanto con Netscape Navigator 4.06 si ebbe la prima implementazione di Javascript 1.3, che era finalmente compatibile con lo standard ECMA da poco finalizzato. Dopodiché Netscape donò il suo codice sorgente a Mozilla, e iniziò una lunga riscrittura del browser per migliorarne le prestazioni (e la manutenibilità). Questa operazione è stata un po’ la fine della storia di Netscape, ma questa è un’altra storia.</p>
<p>Nel 2008 tutti i principali browser (Internet Explorer, Firefox, Safari, Chrome e Opera) implementavano EcmaScript terza edizione. Da allora possiamo dire che la vita degli sviluppatori si è semplificata di molto.</p>
<h2>Conclusioni</h2>
<p>Spero di avere fatto un po’ di chiarezza sulla storia di Javascript! Nei prossimi articoli vi parlerò del BOM e del DOM, e di come sono stati standardizzati e/o implementati dai vari browser.</p>
Il Document Object Model (DOM)
2015-01-08T08:53:26Z
https://michelenasti.com/2015/01/il-document-object-model-dom/
<p>Come abbiamo detto (nell'articolo "<a href="http://michelenasti.com/2015/01/un-breve-riassunto-sulla-storia-di-javascript/">un breve riassunto della storia di Javascript</a>"), Javascript è composto da tre parti:</p>
<ul>
<li>EcmaScript, che definisce la sintassi del linguaggio;</li>
<li>il Document Object Model, oggetto di questo articolo,</li>
<li>e il Browser Object Model, che sviscereremo nella prossima puntata.</li>
</ul>
<p>Il <strong>Document Object Model (DOM)</strong> è una API per l’XML che è stata estesa per l’HTML. il DOM mappa l’intera pagina come una gerarchia di nodi. Considerando un semplice file html, come</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>This is a title!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /> Hello World!<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Questo documento viene rappresentato come un albero la cui radice è il nodo <code><html></code>, i figli di <code><html></code> sono <code><head></code> e <code><body></code>, e così via.</p>
<h2>il DOM è necessario?</h2>
<p>A partire da Internet Explorer 4 e Netscape Navigator 4 (ragazzi, stiamo parlando del giurassico) questi browser iniziarono a supportare delle forme di Dynamic Html (DHTML), grazie al quale potevano modificare il contenuto di una pagina senza ricaricarla. Fu un enorme passo avanti per il web, ma anche un grande problema per gli sviluppatori: i due browser implementavano specifiche diverse, e a farne le spese spesso erano gli utenti. (I problemi non si risolvevano dicendo "vabbè, ora scarico l’altro browser e vedo come si vede la pagina"... Internet era leeeeentiiiiisssiiiiiimooooooo). Siccome non si poteva attendere che uno dei due browser vincesse sull’altro, e per preservare la natura "aperta" del web, il W3C (ente che sovrintende le regole del web) decise di standardizzare il DOM.</p>
<h2>DOM levels</h2>
<p>Il <strong>DOM di livello 1</strong> divenne uno standard nel 1998. Era composto da due parti: il <em>Core</em>, che forniva gli strumenti per mappare un documento XML, e per fare ricerche e manipolazione al suo interno; e il <em>DOM HTML</em>, un’estensione del Core, che aggiungeva metodi specifici per l’HTML. Da notare che i DOM non è specifico a Javascript ma anzi è stato implementato in altri linguaggi.</p>
<p>Con il <strong>DOM di livello 2</strong> si puntava ad aggiungere funzionalità al DOM di primo livello. Fu aggiunto il supporto per il Mouse, per gli eventi della UI, iteratori per poter attraversare il documento, e il supporto per i CSS.</p>
<p>Il <strong>DOM di livello 3</strong> continua ad estendere le funzionalità dei DOM precedenti e introduce anche metodi per il caricamento e il salvataggio dei documenti, e metodi per la validazione. Nel DOM Level 3, il Core è esteso per essere compatibile con tutto lo standard XML 1.0.</p>
<blockquote>
<p>Alcuni parlano anche di <strong>DOM livello 0</strong>, che non è mai stato uno standard, ma anzi rappresenta solo ciò che era il DOM all’epoca di IE4 e NN4.</p>
</blockquote>
<p>Esistono anche altri DOM tarati per altri linguaggi, come SVG, MathML, SMIL, e anche Mozilla ha un suo DOM per la sua interfaccia grafica (XUL). Se volete approfondire Google è vostro amico.</p>
<h2>E i Browser?</h2>
<p>Internet Explorer ha iniziato ad avere un supporto del DOM level 1 a partire dalla versione 5.5 . Fino alla versione 8 non è cambiato granchè, per avere nuove funzionalità conviene partire da IE9. Netscape senza che ne parliamo visto che si è estinto, ma è bene sapere che Firefox dalla versione 3 in poi supporta il Level 1, una grande parte del Level 2, e alcune parti del level 3.</p>
<p>Ci vediamo alla prossima puntata per parlare dello sconosciutissimo BOM !</p>
chiamare una funzione Javascript subito dopo averla definita
2015-01-08T09:14:21Z
https://michelenasti.com/2015/01/chiamare-una-funzione-javascript-subito-dopo-averla-definita/
<p>Consideriamo il seguente esempio:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> clickCount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><br /> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'button#mybutton'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">click</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> clickCount <span class="token operator">++</span><span class="token punctuation">;</span><br /> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">'Clicked '</span> <span class="token operator">+</span> clickCount <span class="token operator">+</span> <span class="token string">' times.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Questa è la semplice definizione di una funzione (anonima) che, quando viene cliccato su un bottone, aumenta un contatore. Ma in grassetto ho evidenziato due parentesi tonde: questa è una sintassi comoda e rapida per indicare che la funzione, una volta definita, deve essere anche subito eseguita. Ricapitoliamo:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span> <br /><span class="token punctuation">}</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">;</span></code></pre>
<p>Questo pattern viene utilizzato quando non vogliamo inquinare il <em>global scope</em> con variabili che potenzialmente possono entrare in conflitto con altre, e quando vogliamo creare nuovi <em>scope</em>. Parleremo degli <em>scope</em> In altri articoli.</p>
JAVA - Convertire un array di byte in un file con FileOutputStream
2015-01-08T10:49:51Z
https://michelenasti.com/2015/01/convertire-un-array-di-byte-in-un-file-java-con-fileoutputstream/
<p>Ok, JasperReports mi pemette di creare un report in pdf anche come array di byte, cosa utile visto che poi il report va a finire nel db. Ma per fare dei test, come faccio a trasformare l'array di byte in file?</p>
<p>Voilà:</p>
<pre class="language-java"><code class="language-java"><span class="token class-name">FileOutputStream</span> fos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"pathname"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />fos<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>myByteArray<span class="token punctuation">)</span><span class="token punctuation">;</span><br />fos<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Vantaggi: non servono librerie aggiuntive per questa operazione.</p>
<p>Svantaggi: qualcuno dice che <a href="http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FileUtils.html#writeByteArrayToFile%28java.io.File,%20byte%5B%5D%29">Apache Commons</a> si fa tutto con una istruzione.</p>
<p>fonte: <a href="http://stackoverflow.com/questions/4350084/byte-to-file-in-java">Stack Overflow - byte[] to file in Java</a></p>
Uscire dalla Comfort Zone per ottenere più successo
2015-01-09T09:11:25Z
https://michelenasti.com/2015/01/uscire-dalla-comfort-zone-per-ottenere-piu-successo/
<p>Sto leggendo un libro interessantissimo su come costruire un Personal Brand, di Frédéric Harper (visitate il suo sito <a href="http://outofcomfortzone.net/">outofcomfortzone.net</a>).</p>
<p>L'idea principale del libro, che l'autore ha ripetuto un milione di volte e sono appena al capitolo 6, è che chiunque può costruirsi un Personal Brand, ossia fare un marchio con se stesso. In realtà il marchio già ce l'abbiamo, basta chiedere ai nostri colleghi/amici per scoprire che un'idea su di noi già c'è. Quindi il libro è principalmente su come portare questo nostro "brand" al next level, per raggiungere gli ambiziosi risultati che ci proponiamo: avanzamento di carriera, più soldi, il lavoro dei sogni, etc.</p>
<p>Non mi soffermerò troppo sul libro, perché scriverò altri articoli a riguardo, oggi parlerò della <strong>Comfort Zone</strong>: sapete cos'è? E' quella "sfera" di atteggiamenti, comportamenti, e situazioni che ci fanno sentire a nostro agio. Ad esempio, se per lavoro ho sempre fatto il programmatore, mi sentirei a mio agio a diventare uno speaker per le conferenze? O team leader? O Project Manager? Ecco, sono sicuro che se ve lo proponessero su due piedi, la risposta di molti è tra il "no" e il "ci devo pensare".</p>
<p>Il brutto è che finché siamo incatenati nella nostra comfort zone, ci sentiamo ovviamente rassicurati dal fatto che faremo cose che già sappiamo fare, e pensiamo che per uscire da questa sfera bisogna essere dei super esperti nell'ambito di destinazione.</p>
<p>Sorpresa: non serve essere dei super esperti. E' difficile trovare superstar di livello galattico, quindi esistono persone disposte a concedervi un giro fuori dalla vostra comfort zone, a patto che voi vogliate mettercela tutta. Loro vi danno fiducia (sanno benissimo che è una cosa che non avete mai fatto!), voi non dovete cercare alibi: dovete imparare in fretta, e molto spesso è così che accade, anche senza che ci sia io a consigliarvelo.</p>
<p>Quando nella mia azienda si è aperta la possibilità di occupare il ruolo di <a href="http://it.wikipedia.org/wiki/Scrum_%28informatica%29">Scrum Master</a>, mi ci sono buttato a capofitto. Non tanto perchè lo sapessi fare (lo avevamo appena adottato!) ma l'idea di fare qualcosa di nuovo mi eccitava terribilmente. Non è stato un percorso facile, in due anni ne ho combinate di cotte e di crude, però è qualcosa che ora ho nel mio bagaglio di esperienze e se dovesse capitare un'altra occasione, non mi sentirei a disagio. E' questo che chiedo anche a voi! Appena c'è un'occasione che vi piace, proponetela al vostro manager, vedete che ne pensa di affidare quel compito proprio a VOI.</p>
<p><img src="https://michelenasti.com/uploads/2015/01/wherethemagichappens.jpg" alt="/uploads/2015/01/wherethemagichappens.jpg" /></p>
<p>le cose fighe accadono proprio lì, fuori dalla nostra comfort zone. <strong>Se facciamo sempre le solite cose, verremo pagati sempre allo stesso modo.</strong> Se vogliamo spezzare questa catena, conviene buttarsi a capofitto!</p>
<p>Che peccato averlo capito solo ora.</p>
Hackerando la caldaia Junkers CERACLASS SMART INCASSO per farla funzionare col termostato NetAtmo
2015-01-11T16:20:32Z
https://michelenasti.com/2015/01/hackerando-la-caldaia-junkers-ceraclass-smart-incasso-per-farla-funzionare-col-termostato-netatmo/
<p>titolo lungo, figuratevi il post 🙂</p>
<p>Tempo fa ho ristrutturato casa e ho installato una caldaia nuova: questa J<a href="http://www.junkers.it/privati/prodotti/dettaglio_prodotto/scheda_prodotto_4425">unkers Ceraclass Smart Incasso</a>. Inutile dire che me l'hanno venduta come la miglior caldaia possibile, considerando che doveva essere incassata nel muro.</p>
<p>Dopodichè, memore del periodo precedente in cui vivevo solo, ho deciso di comprare il <a href="https://www.netatmo.com/it-IT/prodotto/thermostat">termostato NetAtmo</a>, per programmare in modo intelligente la temperatura (e i risparmi) di casa. Il NetAtmo si inserisce nel filone della domotica per tutti, e <strong>permette di controllare la temperatura di casa tramite smarphone</strong>.</p>
<p>Ora, non appena arriva il termostato (e la caldaia) inizia la fila di tecnici e installatori che mi ripetono la stessa cosa: <em>non si può fare... non credo si possano collegare... questa caldaia non è come tutte le altre... etc.</em></p>
<p>La caldaia in questione, in effetti, non è una caldaia come tutte le altre, visto che tutta la configurazione interna la si fà tramite un termostato esterno che è uscito incluso con la caldaia, il TF-25 .</p>
<p><img src="https://michelenasti.com/uploads/2015/01/junkers-bosch-ceraclass-smart-incasso-2.jpg" alt="la caldaia Junkers Ceraclass Smart Incasso, con il suo termostato TF25." /></p>
<p>Il TF-25 è un vero e proprio minicomputer per questa caldaia, infatti permette di impostare la temperatura di mandata, la temperatura dell'acqua sanitaria, la pressione, e molto altro: praticamente tutta la gestione si fa da qui. Si può anche accendere, spegnere o programmare la caldaia in maniera temporizzata, ma non ha la comodità dello smartphone (quindi il netAtmo aveva ancora un senso).</p>
<p>Già l'installatore (colui che installa centinaia di caldaie di questo tipo ogni anno) mi aveva detto che "<em>questa caldaia è molto avanzata, dubito che uno possa usare un termostato diverso da quello che esce con la caldaia".</em> Dubito? Lo sai o non lo sai?</p>
<p>A questo punto ho scritto a NetAtmo, chiedendo lumi sulla mia caldaia e su come collegare il termostato. Loro mi hanno risposto che si, si poteva collegare, ma la spiegazione che mi hanno dato non riesco ad applicarla (che manuale avranno visto?).</p>
<h3>Come funziona il TF25 (e perchè non va bene per il NetAtmo)</h3>
<p>La prima cosa che abbiamo provato a fare è stata scollegare il TF25 e collegare il NetAtmo, come se fosse un normale termostato da muro. Il TF25 è connesso tramite due fili al muro, e noi pensavamo fossero i fili che chiudevano il circuito. Invece no: i due fili sono un bus su cui il TF25 invia i segnali di controllo alla caldaia (temperatura alla quale operare, programmi impostati, etc)</p>
<h3>Come funziona invece il NetAtmo</h3>
<p>il netatmo funziona come tutti i termostati normali: quando la temperatura di casa è minore della temperatura impostata, il circuito si chiude. Per verificarlo abbiamo usato un tester.</p>
<p>Se il netAtmo funziona come tutti i termostati del mondo, mentre il TF25 no, dove lo collego? Stavo per rivenderlo, quando un amico su Facebook (che io definisco "hacker delle caldaie") mi spiega passo passo cosa dovrei fare, manuale alla mano. E quindi ...</p>
<h2>Dove collegare i termostati classici?</h2>
<p>Il relay del netAtmo è composto da quattro fili, due per chiudere il circuito della caldaia (e attivarla) e due per alimentare il relay. I primi due fili bisogna inserirli negli inserti superiori, come mostrato nell'immagine in basso, mentre quelli dell'alimentazione devono essere messi insieme a quelli che alimentano la caldaia. <strong>Spegnete l'alimentazione alla caldaia altrimenti facciamo una bella frittura.</strong></p>
<p><img src="https://michelenasti.com/uploads/2015/01/fili-ceraclass.jpg" alt="fili-ceraclass" /></p>
<p>Nella seconda immagine invece vedete che bisogna alzare il ponticello n.8 per attivare il circuito che abbiamo appena creato.</p>
<p><img src="https://michelenasti.com/uploads/2015/01/ceraclass-controlli.jpg" alt="ponticelli" /></p>
<p>Facile no?</p>
<p>.... no! Non per me che non avevo mai messo mano a queste cose.</p>
<p>Tutte queste informazioni sono scritte sul manuale della caldaia, ma sfortunatamente non tutti gli elettricisti hanno voglia di leggerselo. Figuratevi io che manco ci capisco niente.Tutti pigri tranne il mio "hacker" della caldaia, uno che la domotica è il suo pane quotidiano, quindi se avete bisogno di assistenza, vi metto in contatto con lui 😉</p>
Pdfmake : libreria JS per creare PDF
2015-01-13T13:30:46Z
https://michelenasti.com/2015/01/pdfmake-libreria-js-per-creare-pdf/
<p>Ecco a voi il task della settimana: generare <em>on the fly</em> un <strong>pdf</strong> con il contenuto di una tabella.</p>
<p>Quale libreria scegliere?</p>
<ul>
<li><a href="https://community.jaspersoft.com/"><strong>JasperReports</strong></a> non è adatto allo scopo: bisognerebbe spedire il contenuto della tabella sul backend, e far stampare il report al server; ma soprattutto, se l'applicazione contiene 100.000 tabelle, c'è il rischio di dover preparare 100.000 template? (Aiuto miei cari friends della community, sinceramente non sono un super esperto di JasperReports!). In ogni caso, i dati sono in genere già disponibili sul client; possibili che non ci sono soluzioni "native" in js?</li>
<li><a href="https://parall.ax/products/jspdf"><strong>jsPDF</strong></a> è un'altra libreria presa in considerazione. E' Javascript, è frontend, è leggera. Il problema è che non è capace di renderizzare l'HTML, quindi per creare la tabella avrei dovuto disegnarla linea per linea. Non un compito banale. Ad esempio, per scrivere del testo in una certa posizione, bisogna specificare la posizione di ogni oggetto: <code>doc.text(35, 25, "Paranyan loves jsPDF");</code>. Un delirio di numeri! E poi tutti quei problemi come spaziatura... proporzioni ... overflow del testo... insomma è quasi una tesi di laurea! Scartato.</li>
<li><a href="http://pdfmake.org/#/"><strong>Pdfmake</strong></a> l'ho scoperto per caso. Nella pagina web di <a href="http://ui-grid.info/">UI-Grid</a> (una tabella per Angular) c'era questo import, ma non trovavo nessun riferimento sul sito. Così sono andato sul sito ufficiale e ho provato a capirci di più.</li>
</ul>
<h2>pdfmake, wow</h2>
<p>Iniziamo con i vantaggi. E' <strong>javascript puro</strong>, e funziona anche lato server con NodeJs anche se a noi interessa nel browser (quanta roba la community NodeJs sta rilasciando, cose utilissime sul backend, che però migliorano la vita del frontend!).</p>
<p>E' <strong>dichiarativo</strong>, ossia piuttosto che dire pixel per pixel dove andare ad inserire immagini, testo, linee e tabelle, il documento viene "preparato" quasi come se fosse un HTML. Ci si può dunque focalizzare sul contenuto, e dopo aver dato qualche generica istruzione di layout, il PDF è bello e servito.</p>
<p>Vi devo parlare anche degli svantaggi però. Uscire dal seminato è difficile: la cosa positiva è che a me non serve. Un'altra cosa veramente brutta è che se qualcosa non è stata configurata a dovere, non vi è alcuna eccezione in console, nè niente che faccia capire cosa è accaduto al nostro pdf. Semplicemente non si apre. (E una variabile viene impostata a false). Nei miei test ho creato per errore tabelle con 3 colonne header e 4 colonne dati, e il pdf non veniva generato (e neanche gli errori).</p>
<p>Ma ricordiamoci il nostro obiettivo. Noi vogliamo stampare il contenuto di una tabella html, renderizzata tramite la UI-Grid di Angular. Quindi l'applicazione che vi mostro è Angular based.</p>
<h3>test.html</h3>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">ng-app</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>//code.jquery.com/jquery-2.1.3.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-touch.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-animate.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ui-grid.info/docs/grunt-scripts/csv.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ui-grid.info/docs/grunt-scripts/pdfmake.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ui-grid.info/docs/grunt-scripts/vfs_fonts.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ui-grid.info/release/ui-grid-unstable.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://ui-grid.info/release/ui-grid-unstable.css<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>main.css<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">ng-controller</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>MainCtrl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid1<span class="token punctuation">"</span></span> <span class="token attr-name">ui-grid</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ data: myData }<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">ng-click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stampa()<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>stampiamo il pdf!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Una semplice pagina HTML, come potete vedere sto importando <code>pdfmake.js</code> e <code>vfs_fonts.js</code> , necessari per renderizzare i pdf. Inoltre abbiamo aggiunto un bottone che chiama la funzione <code>stampa()</code>.</p>
<h3>main.css</h3>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span><br /> <span class="token property">width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span><br /> <span class="token property">height</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>Niente di particolare qui, stiamo semplicemente dando le definizioni della griglia.</p>
<h3>app.js</h3>
<p>A riga 78 viene definito il documento PDF: vi conviene partire da lì, dalla variabile <code>dd</code>, e poi nelle funzioni <code>table(data, columns)</code> e <code>buildTableColumns(data, columns)</code>.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> app <span class="token operator">=</span> angular<span class="token punctuation">.</span><span class="token function">module</span><span class="token punctuation">(</span><span class="token string">'app'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'ngTouch'</span><span class="token punctuation">,</span> <span class="token string">'ui.grid'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />app<span class="token punctuation">.</span><span class="token function">controller</span><span class="token punctuation">(</span><span class="token string">'MainCtrl'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'$scope'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">$scope</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><br /> <span class="token comment">//questo array contiene degli oggetti che sono simili a quelli </span><br /> <span class="token comment">//che solitamente si ricevono dal server. </span><br /> $scope<span class="token punctuation">.</span>myData <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token string-property property">"firstName"</span><span class="token operator">:</span> <span class="token string">"Cox"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"lastName"</span><span class="token operator">:</span> <span class="token string">"Carney"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"company"</span><span class="token operator">:</span> <span class="token string">"Enormo"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"employed"</span><span class="token operator">:</span> <span class="token boolean">true</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token string-property property">"firstName"</span><span class="token operator">:</span> <span class="token string">"Lorraine"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"lastName"</span><span class="token operator">:</span> <span class="token string">"Wise"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"company"</span><span class="token operator">:</span> <span class="token string">"Comveyer"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"employed"</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token string-property property">"firstName"</span><span class="token operator">:</span> <span class="token string">"Nancy"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"lastName"</span><span class="token operator">:</span> <span class="token string">"Waters"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"company"</span><span class="token operator">:</span> <span class="token string">"Fuelton"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"employed"</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//questa è la funzione che genera il pdf, una volta cliccato</span><br /> $scope<span class="token punctuation">.</span><span class="token function-variable function">stampa</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <br /> <span class="token comment">//headers contiene il titolo della tabella. Viene ripetuto su</span><br /> <span class="token comment">//più pagine, quindi è importante isolarlo. Vedete la funzione </span><br /> <span class="token comment">//getHeaders() per dettagli. </span><br /> <span class="token keyword">var</span> headers <span class="token operator">=</span> <span class="token function">getHeaders</span><span class="token punctuation">(</span>$scope<span class="token punctuation">.</span>myData<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//fingiamo di prendere i dati dal server... </span><br /> <span class="token keyword">var</span> externalDataRetrievedFromServer <span class="token operator">=</span> $scope<span class="token punctuation">.</span>myData<span class="token punctuation">;</span><br /><br /> <span class="token comment">//in questa funzione costruiamo il body della tabella (con i dati).</span><br /> <span class="token keyword">function</span> <span class="token function">buildTableBody</span><span class="token punctuation">(</span><span class="token parameter">data<span class="token punctuation">,</span> columns</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> body <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//aggiungiamo gli header, come prima riga della tabella</span><br /> body<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>columns<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//per ogni riga... </span><br /> data<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">row</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> dataRow <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token comment">//aggiungiamo le colonne </span><br /> columns<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">column</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">//nel mio caso, il nome delle proprietà è scritto nel </span><br /> <span class="token comment">//campo propertyName delle column. </span><br /> dataRow<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>row<span class="token punctuation">[</span>column<span class="token punctuation">.</span>propertyName<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token comment">//costruita la riga, la aggiungiamo al body</span><br /> body<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>dataRow<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">return</span> body<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token comment">//in questa funzione creiamo la table. Notate che passiamo </span><br /> <span class="token comment">//sia i dati, sia le colonne. </span><br /> <span class="token keyword">function</span> <span class="token function">table</span><span class="token punctuation">(</span><span class="token parameter">data<span class="token punctuation">,</span> columns</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">table</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">//headerRows indica quante righe fanno parte </span><br /> <span class="token comment">//dell'header. </span><br /> <span class="token literal-property property">headerRows</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token comment">//qui chiamiamo la funzione a riga 41 per </span><br /> <span class="token comment">//costruire i dati contenuti nella tabella </span><br /> <span class="token literal-property property">body</span><span class="token operator">:</span> <span class="token function">buildTableBody</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> columns<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">//la variabile dd contiene tutto il documento da renderizzare</span><br /> <span class="token keyword">var</span> dd <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">content</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Tabella di prova'</span><span class="token punctuation">,</span> <span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'header'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">table</span><span class="token punctuation">(</span>externalDataRetrievedFromServer<span class="token punctuation">,</span> headers<span class="token punctuation">)</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">styles</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /><br /> <span class="token literal-property property">tableHeader</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">bold</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">fontSize</span><span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">color</span><span class="token operator">:</span> <span class="token string">'black'</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token comment">// Apre il PDF in una nuova finestra</span><br /> <span class="token keyword">var</span> outputCreatePdf <span class="token operator">=</span> pdfMake<span class="token punctuation">.</span><span class="token function">createPdf</span><span class="token punctuation">(</span>dd<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>outputCreatePdf<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Stampa il PDF (Chrome-only)</span><br /> <span class="token comment">//pdfMake.createPdf(docDefinition).print();</span><br /><br /> <span class="token comment">// download il PDF ( Chrome-only)</span><br /> <span class="token comment">//pdfMake.createPdf(docDefinition).download('optionalName.pdf');</span><br /><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">//sostituisce i nomi da camelCase a Camel case (non funziona troppo bene!)</span><br /><span class="token keyword">function</span> <span class="token function">toTitleCase</span><span class="token punctuation">(</span><span class="token parameter">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> str<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\w\S*</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">txt</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> txt<span class="token punctuation">.</span><span class="token function">charAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> txt<span class="token punctuation">.</span><span class="token function">substr</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//estrapola gli header dagli oggetti definiti sopra</span><br /><span class="token keyword">function</span> <span class="token function">getHeaders</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> firstObject <span class="token operator">=</span> data<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">var</span> headers <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> key <span class="token keyword">in</span> firstObject<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <br /> <span class="token comment">//metodo BARBARO per togliere </span><br /> <span class="token comment">// questa property aggiunta da Angular </span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>key <span class="token operator">!==</span> <span class="token string">'$$hashKey'</span><span class="token punctuation">)</span> <br /> headers<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token function">toTitleCase</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'tableHeader'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">propertyName</span><span class="token operator">:</span> key <span class="token comment">//contiene il nome della property</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> console<span class="token punctuation">.</span><span class="token function">table</span><span class="token punctuation">(</span>headers<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> headers<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//trasformo gli oggetti in array di dati </span><br /><span class="token keyword">function</span> <span class="token function">getData</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> firstObject <span class="token operator">=</span> data<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">var</span> newData <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> key <span class="token keyword">in</span> firstObject<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>key <span class="token operator">!==</span> <span class="token string">'$$hashKey'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> data<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>newData<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">===</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span><br /> newData<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> newData<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>data<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> newData <span class="token operator">=</span> $<span class="token punctuation">.</span><span class="token function">makeArray</span><span class="token punctuation">(</span>newData<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> console<span class="token punctuation">.</span><span class="token function">table</span><span class="token punctuation">(</span>newData<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> newData<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<h3>Conclusioni</h3>
<p>Questo codice è quello che ho realizzato oggi per fare una piccola demo sulle capacità di questa libreria. Non è elegante, non è chiarissimo, ma mi è costato tanta fatica (e poi funziona). Penso che in futuro ritorno su questo esempio, specialmente ora che devo realizzare la vera e propria funzionalità, per tutte le tabelle del sistema!</p>
Codemotion 2015
2015-01-17T16:08:36Z
https://michelenasti.com/2015/01/codemotion-2015/
<p>Il 27-28 Marzo 2015 sarò al <a href="http://rome2015.codemotionworld.com/">Codemotion</a> di Roma, come "sviluppatore semplice" (magari in futuro come talker) 😀 !</p>
<h2>cos'è il Codemotion?</h2>
<p>Il codemotion è una conferenza organizzata in più parti del mondo. In Italia viene organizzata a Roma e a Milano, per ora. E' proprio a Roma che sono andato, nel 2014, solo di sabato. In generale dura due giorni, quasi sempre un venerdì e un sabato.</p>
<p>Le conferenze sono così organizzate: ci sono alcuni filoni, come ad esempio sviluppo mobile, backend, devops, gaming, metodologie... Per ognuno di questi filoni vengono programmati dei talk in varie aule, e quasi sempre in contemporanea, costringendo gli iscritti a dover "pianificare prima" cosa andare a vedere. (La verità, è che sarebbe difficile trovarli tutti interessanti, ogni sviluppatore è per sua natura diverso da un altro, e quindi ci si può permettere di scegliere quali talk seguire).</p>
<p>Codemotion non è solo conferenze: ci sono altre cose che vale la pena fare, e vedere. Iniziamo dalla sponsorizzazione delle grandi aziende presenti sul posto: l'elenco lo trovate sul sito, ma sappiate che aziende come Intel, IBM, etc sono presenti con i loro stand, con ricchi gadget per noi nerd.</p>
<p>Anche lo spazio dedicato ai Makers merita di essere visto: stampanti 3D, arduino, scarpe intelligenti (!), e cose così. Chi realizza qualcosa di ultra-figo viene a mostrarlo lì.</p>
<p>E infine un lunghissimo spazio dedicato alle startup, per presentare i loro business, oppure agli sviluppatori di giochi, con la possibilità di provarli.</p>
<h2>Ma ne vale la pena?</h2>
<p><strong>Secondo me SI</strong>. Io sono uno sviluppatore, e ho bisogno di sapere in che modo il mercato si sta muovendo, per essere sempre aggiornato. Lo sappiamo tutti che 5 anni fa facevamo i siti web in un modo, che oggi è totalmente obsoleto. 5 anni fa non c'erano neanche le piattaforme mobile come le vediamo ora. E allora?</p>
<p>Se aspettiamo che nelle nostre aziende vengano a proporci dei corsi di aggiornamento, perché il management è "illuminato" e ha soprattutto intuito quale sarà il prossimo trend nell'informatica, allora possiamo tranquillamente invecchiare insieme alla nostra azienda: non succederà. E al prossimo progetto verranno assunti consulenti nuovi perché costano meno, conoscono le tecnologie attuali, e soprattutto prenderanno il NOSTRO posto (che fino a poco fa ritenevamo intoccabile).</p>
<h2>E poi?</h2>
<p>Non sarà il Codemotion ad aggiornarvi: al Codemotion vi parleranno di questa o quella libreria, poi dovete rimboccarvi le maniche e studiarvi le cose a casa. Ma al Codemotion potete fare conoscenza con gente interessante, gente che vuole programmare <em>the next big thing</em>, e magari attuare quell'idea vi stuzzica.</p>
<h2>La mia (breve) esperienza</h2>
<p>L'anno scorso sono andato perchè alcuni colleghi mi hanno coinvolto, e ne sono rimasto affascinato. Si parlava di tecnologia, quella vera, anche in maniera mai banale. Soprattutto, <strong>non sono un appassionato di gaming, ma ho seguito tanti talk sull'argomento</strong> perché ho sempre voluto sapere come si realizza un videogioco.</p>
<p>Potrei citarvi quello che ho sentito dire ad uno speaker: "stavamo cercando un modo per realizzare una console per visualizzare i log di rete, e ci serviva qualcosa che funzionasse in tempo reale, ...alla fine la libreria più semplice ci è sembrata questa, che si usa per fare videogiochi". Impara l'arte e mettila da parte!</p>
How to style a button to upload a file
2015-01-20T20:44:52Z
https://michelenasti.com/2015/01/how-to-style-a-button-to-upload-a-file/
<p>This is what I'm doing right now at work: <em>trying to style an <strong>input button</strong> to upload a file</em>.</p>
<p>So, if you simply type the code like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>this is what you get:</p>
<p><img src="https://michelenasti.com/uploads/2015/01/upload-no-style.png" alt="The upload button without styling" /></p>
<p>The problem is: how do you style the button ( written here in Italian, "scegli file" - "choose file") to match the style of your website?</p>
<p>For example, in my works I use <a href="http://getbootstrap.com/">bootstrap</a> like half dozen million of people of the world does. I want the style of <em>this</em> button to match the style of the others. Also, I don't want to see the "<em>nessun file selezionato</em>" ("no file selected") written aside (it can't be styled too!).</p>
<h3>The solution</h3>
<p>This is something I have discovered on <a href="http://stackoverflow.com/a/25825731/1020090">StackOverflow</a> and by now it is my favorite hack.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>form-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inputFile<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>btn btn-primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>inputFile<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>file<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span>none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br /> Select File<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>This is how it is rendered by the majority of browsers:</p>
<p><img src="https://michelenasti.com/uploads/2015/01/upload-button-styled.png" alt="How the styled button will be displayed" /></p>
<p>The first "this is a button" is a normal html <code><button></code> element. the second is the file uploader button.</p>
<h3>How the hack works?</h3>
<p>When you wrap a <code><label></code> around an <code><input></code> element, if you click on the label, the click is propagated to the input element pointed by the <code>for="..."</code> attribute (matching the input id).</p>
<p>Another help is coming from the <code>style="display:none;"</code> attribute. It will hide the input element from the screen, so that we only see the label for the input.</p>
<h3>Compatibility</h3>
<p>Boring part. It seems that it doesn't work very well with IE8 and before. Have a look on Stack Overflow on how to make it work. I don't care, since I don't use IE and I generally target my work to the latest browsers and platforms.</p>
A video that makes Italians proud of their Country
2015-01-24T18:51:46Z
https://michelenasti.com/2015/01/a-video-that-makes-italians-proud-of-their-country/
<p>This video is suddenly gaining its' rise in the Italian Startup Community. A video "to show to our foreign investors", as someone said.</p>
<p>The truth is that Italy has its problems, but speaking only of them is not taking us anywhere. We - Italians - have lost the love for our country. We never (or rarely) think of _being constructive._We always believe that only a true Revolution (the one with Che Guevara & Mao) will disrupt everything, forgetting what we already enjoy (and love).</p>
<p>May I send a message to my political class? (Of course, they do NOT speak English, but Google Translate is aviable on their iPads). Now that we have made the video, let's make something (incredible!) here at the South. Maybe South Italy could become the Terra Franca (like San Francisco!) for Startups! Think about it.</p>
Imparare a programmare come ... chef Cannavacciuolo
2015-01-29T20:13:20Z
https://michelenasti.com/2015/01/imparare-a-programmare-come-chef-cannavacciuolo/
<p>Quando la sera riesco a tornare a casa alle 19, la prima cosa che faccio è accendere la tv e sintonizzarmi su "Cucine da Incubo", versione italiana. Potrò godermi solo gli ultimi 5-10 minuti, ma ne vale la pena, perché posso ammirare la cucina dello chef <a href="http://www.antoninocannavacciuolo.it/">Antonino Cannavacciuolo</a>.</p>
<p>Il programma in sé è un po' ripetitivo, il canovaccio è sempre lo stesso, e se non fosse per lo chef sarebbe addirittura palloso. Quello che infatti mi cattura è il suo modo di motivare le persone, quella pacca sulla spalla che solo un napoletano può immaginare; e poi il modo in cui fa comprendere allo spettatore che cosa manca per avere successo: <strong>la passione</strong>. Quella voglia di lavorare divertendosi, provando sempre a migliorarsi, a superarsi, a metterci la faccia.</p>
<p>Ma... <strong>si può programmare con passione?</strong> Nella mia vita da informatico (brevissima, per ora) mi è capitato poche volte di potermi superare. C'entra il fatto che spesso lavoriamo a cose <em>noiose e ripetitive</em>, e non ci viene nemmeno chiesta l'opinione per provare a risolvere il problema in maniera innovativa. Inoltre <em>lavoriamo con delle scadenze</em>, e questo ci impedisce di fare del nostro meglio, visto che se consegniamo in ritardo salta tutto. E poi, devo essere sincero, tutti i <em>nostri sforzi di fare qualcosa di eccezionale viene mitigata da quei programmatori aziendalisti</em>, quelli che fanno copia&incolla senza capire, e che soprattutto non fanno mai una proposta, non si aggiornano, non si confrontano.</p>
<p>Quelle persone lì, ahimè, sono spacciate. Se provassero a cambiare lavoro troverebbero enormi difficoltà, e l'unico sbocco che avrebbero è la carriera interna alla stessa azienda o andare in altre aziende con progetti simili. Ma il mercato ora è molto stagnante... e loro, comunque, non cambieranno lavoro.</p>
<p>La mia ricetta del <strong>programmatore perfetto</strong>? <strong>100% passione</strong> - e questo s'è capito. E poi aggiungiamo <strong>33% voglia di fare le cose per bene, 33% capacità di rispettare le scadenze, e 33% voglia di divertirsi</strong>.</p>
<p>Io, lo ammetto, sono sbilanciato: la voglia di divertirmi e di fare le cose per bene prevalgono leggermente. E una cosa mi piace davvero, <strong>la passione mi divora</strong>.</p>
Pro e contro di AngularJs dopo 2 mesi di lavoro
2015-02-02T09:55:11Z
https://michelenasti.com/2015/02/pro-e-contro-di-angularjs-dopo-2-mesi-di-lavoro/
<p><em>Questa è una chat tra me e il mio ex-collega/amico <a href="https://www.linkedin.com/profile/view?id=9483864" title="Davide Antelmo">Davide</a>, sull'esperienza fatta con <a href="https://angularjs.org/">AngularJs</a>. Forse anche loro svilupperanno un POC (proof of concept) e voleva un po' di info. Credo che queste "opinioni" possano servire anche ad altri. Enjoy 🙂</em></p>
<blockquote>
<p><strong>D.:</strong> Come vi state trovando con angularjs?</p>
</blockquote>
<p><strong>Io:</strong> la cosa di AngularJs è lunghetta; ci sono pro e contro. Di sicuro Angular è un framework all-or-nothing ... Non si può usare solo una parte di Angular, come il data binding, senza usare ad es. le direttive, o la dependency injection. Ci sono parti di Angular che si possono saltare, ma il "core" è abbastanza ampio. Quindi lo si deve imparare per forza tutto prima di capirne tutte le potenzialità.</p>
<p>Un altro svantaggio di Angular è che <strong>è JavaScript</strong>, e JavaScript è un linguaggio da studiare completamente, ossia il modello a oggetti, i prototype, gli scope, gli === al posto di == ... Pensa, ho scoperto dopo mesi di lavoro che alcuni ignorano la possibilità di provare codice nella console del browser!</p>
<p>Ci sono anche alcuni vantaggi, però: è ormai il framework più diffuso a livello mondiale, da un po' esistono migliaia di estensioni, direttive, filtri, etc. che qualcuno ha già scritto e fa proprio quel che vuoi tu.</p>
<p>Durante lo sviluppo ho sentito due mancanze principali: <strong>un esperto di Angular che ci guidasse</strong> (ho dovuto imparare quasi tutto da me!), e senza conoscerne i reali vantaggi è stato scelto forse un po' per moda... scelta azzeccata comunque. E poi la <strong>mancanza di linee guida</strong> per tutti gli sviluppatori. Specialmente all'inizio ognuno faceva le cose di testa sua. Chi usava jQuery, chi usava Bootstrap classico (quando c'è <a href="http://angular-ui.github.io/bootstrap/">angular-ui</a>), ... Queste cose però si risolvono , diciamo.</p>
<p>Quel che mi manca invece è la possibilità di fare refactoring efficienti ... L'unico IDE decente per Angular (o meglio per JavaScript) è <a href="https://www.jetbrains.com/webstorm/">WebStorm</a>, che si paga (45$), ma nel mio team nessuno l'ha voluto adottare ... Troppa resistenza. E gli errori di sintassi si scoprivano solo a runtime, quindi trial&error.</p>
<p>Alla fine della fiera cmq il software lo stiamo realizzando, i problemi li abbiamo tutti aggirati pure con una buona infrastruttura, e l'esperienza fatta è molto preziosa ... Non saprei che altro dirti; i problemi di "efficienza" che tanti dicono su Angular noi non li abbiamo visti!</p>
<blockquote>
<p><strong>D.:</strong> scusa e che ide avete usato? Cmq anche noi stiamo per far partire un POC su Angular. Durante il POC sceglieremo se usare <a href="http://brackets.io/">Brackets</a>, <a href="https://atom.io/">Atom</a>, o addirittura <a href="http://www.visualstudio.com/en-us/products/free-developer-offers-vs.aspx">Visual Studio community edition 2013</a></p>
</blockquote>
<p><strong>Io:</strong> Il migliore in assoluto è WebStorm</p>
<blockquote>
<p><strong>D.:</strong> ovviamente imbarcarsi su AngularJs senza una base solida su javascript è un grosso problema. Chiaro , ti parlavo di quelli free. Quelli che ti dico io sono tutti free.</p>
</blockquote>
<p><strong>Io:</strong> Gli sviluppatori sono abituati a Eclipse per il backend e quasi tutti usano eclipse anche per il frontend. Difficilmente li convinci se non gli fai vedere che funziona ctrl+spazio (che comunque in Eclipse JavaScript non c'è!) o altre amenità. Ho pure mostrato che in WebStorm c'è, anche se non funziona benissimo, e comunque manco l'hanno voluto usare... Ho visto un pò di brackets ed è OK... Non so visual studio etc come vanno</p>
<blockquote>
<p><strong>D.:</strong> cmq anche io ho letto diversi articoli sui famosi ‘problemi di perfomance' di angularjs. Quante UI aveva il progetto che avete usato?</p>
</blockquote>
<p><strong>M:</strong> Un centinaio di UI. Era una vecchia applicazione Visual BASIC portata in web</p>
<blockquote>
<p><strong>D.:</strong> beh good avrete modularizzato bene... ma avete usato anche <a href="http://requirejs.org/">RequireJs</a> per caricare asincronamente i moduli?</p>
</blockquote>
<p><strong>Io:</strong> No niente RequireJs</p>
<blockquote>
<p><strong>D.:</strong> avete usato qualche template per la struttura del progetto ? Tipo <a href="https://github.com/angular/angular-seed">angular seed</a>? o avete usato qualcosa di più ‘enteprise' ?</p>
</blockquote>
<p><strong>M:</strong> una sorta di "template" ce lo siamo inventati noi. Su questa parte non siamo stati troppo smart ... Eravamo all'inizio e non sapevamo che fare. Dovevamo "buttare le mani"</p>
<blockquote>
<p><strong>D:</strong> io penso che useremo qualcosa di questo tipo: <a href="https://github.com/johnpapa/gulp-patterns" target="_blank" rel="nofollow">https://github.com/johnpapa/gulp-patterns</a></p>
</blockquote>
<p><strong>M:</strong> Se potessi tornare indietro userei <a href="http://yeoman.io/">yeoman</a> che ho usato a casa</p>
<blockquote>
<p>**D.: **<a href="http://slides.com/thomasburleson/using-requirejs-with-angularjs#/" target="_blank" rel="nofollow">http://slides.com/thomasburleson/using-requirejs-with-angularjs#/</a> <a href="https://github.com/ThomasBurleson/angularjs-Zza-BMEAN" target="_blank" rel="nofollow">https://github.com/ThomasBurleson/angularjs-Zza-BMEAN</a></p>
</blockquote>
<p>**M: **Si si purtroppo sono cose che ho letto DOPO che ho iniziato a lavorare su angular. Purtroppo non ho partecipato alla fase di setup del progetto. Ah dimenticavo, un altro svantaggio probabile è che A<a href="http://ng-learn.org/2014/03/AngularJS-2-Status-Preview/">ngularJs 2 sarà una cosa totalmente diversa</a></p>
<blockquote>
<p><strong>D.: s</strong>i visto</p>
</blockquote>
<p><strong>Io:</strong> Ma non uscirà subito e cmq angular 1 sarà supportato per molto tempo ancora</p>
<blockquote>
<p><strong>D.:</strong> cmq il modo in cui si fanno le web application (anche con backend Java) sta cambiando. Insomma <a href="http://en.wikipedia.org/wiki/Single-page_application">SPA</a>: oggi è AngularJs 1,domani AngularJs 2 o web components, ma architetturalmente il futuro è questo. L'era delle pagine generate dal server è finito 🙂</p>
</blockquote>
<p><strong>Io:</strong> Il web si interroga come ha fatto angularJs a vincere ... ci sono altri progetti concorrenti che qualitativamente stanno lì, come <a href="http://backbonejs.org/">Backbone</a> e <a href="http://emberjs.com/">Ember</a>... Bo</p>
<blockquote>
<p><strong>D.:</strong> Perchè AngularJs, secondo me, è più enterprise. Le due cose ‘enterprise' che ha di più sono le direttive e i moduli. Ti permette di strutturare, riusare, testare... poi ok c'è anche la dependency injection... ah, questi javisti che si mettono a inventare framework javascript!</p>
</blockquote>
<p><strong>Io:</strong> a quanto pare è stato scritto da un Javista ...</p>
<blockquote>
<p><strong>D.:</strong> Bravo!</p>
</blockquote>
<p><strong>Io:</strong> Mi hai anticipato, ho mia moglie che mi sta tallonando 😀</p>
<blockquote>
<p><strong>D.:</strong> idem! e allora buona serata, e grazie per i feedback!</p>
</blockquote>
WebApp nel tempo libero con approccio MEAN
2015-02-06T10:17:38Z
https://michelenasti.com/2015/02/webapp-nel-tempo-libero-con-approccio-mean/
<p>E' una di quelle domande poste da un amico che ha letto il mio precedente articolo (<a href="http://michelenasti.com/2015/02/pro-e-contro-di-angularjs-dopo-2-mesi-di-lavoro/">pro e contro di Angular dopo due mesi di lavoro</a>). Lui è un ex-collega universitario che da qualche tempo non ha più la possibilità di programmare; siccome ha voglia di aggiornarsi, ha chiesto un po' in giro quali sono gli ultimi trend.</p>
<p>Quel che voleva realizzare è un piccolo software per la gestione della biblioteca presso cui presta servizio. E allora sono partito con la prima domanda: <em>dato che vuoi approfittarne per aggiornarti, hai <strong>molto</strong> tempo libero?</em></p>
<p>E poi via con le tristi ma necessarie frasi a effetto: <strong>il tempo delle pagine generate lato server è finito</strong>, e non sono <s>solo</s> io ad averlo sentenziato 🙂</p>
<p>Non è che i server non esistono più, anzi; il loro ruolo adesso è fortemente cambiato. I server ormai prendono i dati dal DB, li validano, li assemblano, li trasformano, e li mandano alla UI che si preoccupa invece di renderizzare le pagine.</p>
<p>A inizio anno ho risposto a un sondaggio on line che dà l'idea di dove vuole andare a parare il <a href="http://tutorialzine.com/2014/12/the-languages-and-frameworks-that-you-should-learn-in-2015/">web development nel 2015</a>: Il vincitore a mani basse è <strong>NodeJS</strong>, motivo per cui bisogna assolutamente essere dei maestri di Javascript.</p>
<p>Se si approccia NodeJS bisogna imparare uno stack tecnologico completamente rivoluzionato rispetto al vecchio caro LAMP (<strong>L</strong>inux, <strong>A</strong>pache, <strong>M</strong>ySql, <strong>P</strong>hp). Il nuovo approccio viene definito "<strong>MEAN"</strong> che sta per <strong>M</strong>ongoDB, <strong>E</strong>xpress, <strong>A</strong>ngular, <strong>N</strong>odeJS.</p>
<p>Mi vorrei soffermare giusto un attimo sul fatto che in MEAN manchi Javascript nella definizione, ma di fatto c'è. <strong>Javascript è alla base di tutte queste tecnologie</strong>; una volta (all'università) ci veniva proposto di usare JS solo per fare un po' di validazione lato client e niente più; invece è un Linguaggio di Programmazione come tutti gli altri - Java, C, PHP, etc. - e come tale va <em>studiato approfonditamente</em>. Tra le tante cose è un linguaggio <em>funzionale</em>, ci sono almeno quattro modi per creare un oggetto, ed è importante conoscerli tutti; è dinamico, è prototipizzato, ed ha un concetto di "scope" da afferrare bene!</p>
<p>I vecchi problemi di compatibilità dei browser, da IE9 in poi, sono stati praticamente risolti (in passato ogni browser faceva di testa sua, e gli standard non venivano MAI rispettati); ora sviluppare webapp è un piacere, e con browser disponibili in ogni smartphone diventa facilissimo trasformare una webApp in un'App.</p>
<p>Cosa manca all'elenco? Ah si! <a href="http://getbootstrap.com/">Bootstrap</a> 🙂 nessuno di noi nasce web designer, motivo per cui utilizzando Bootstrap il nostro sito parte con un livello di bellezza decente.</p>
<h2>Cazzate! Questa roba non diventerà mai mainstream</h2>
<p>Forse è vero. Javascript stesso ci ha messo 10 anni per passare da chicca presente nei browser a linguaggio a sé stante. Ma il succo non è di imparare qualcosa per poi riportarla nel mondo del lavoro; l'intramontabile Java e gli immortali database relazionali dureranno molto più a lungo delle nostre stesse vite. Ciò che conta davvero è capire la filosofia alla base di questi nuovi approcci, e perché sono così diversi dallo sviluppo che si faceva anche solo 5 anni fa.</p>
<p>Se nelle università o nei corsi <em>professionali</em> non accennano neanche un po' ad Ajax, o spiegano <em>solo</em> la generazione di pagine lato server, allora è il momento che vi aggiornate da voi.</p>
Pay attention to the ".active" progress bar class in Bootstrap
2015-02-11T13:15:36Z
https://michelenasti.com/2015/02/attenti-alla-classe-active-delle-progress-bar-bootstrap-3/
<p>Yesterday a coworker calls me because he wants to understand why, <strong>after loading the search results on a given page, we had an active CPU running</strong>. In fact, after the data is loaded, the CPU goes from 10% to20% without any apparent cause.</p>
<p>Astonished, we started to find out all the possible causes to this.</p>
<p>Is this the famous "<em>Angular Performance Problem</em>" that we have all read on the web (but never found in practice)? Well, the profiler said that nothing was happening on the page.</p>
<p>Maybe we are loading too much data from the search? 87 kb is not that much, we have loaded far more data without problems.</p>
<p>Suddenly we have a brainwave: <strong>on that page we have an animated progress bar for each search result.</strong> With developer tools I delete all this progress bars <em>et voilà</em>: <strong>CPU calms down to 0%.</strong></p>
<p><img src="https://michelenasti.com/uploads/2015/02/progress-bar-bootstrap.png" alt="Bootstrap 3 progress bar" /></p>
<p>Our error has been to leave the class <code>active</code> on our progress bars, and if you go on the <a href="http://getbootstrap.com/components/#progress-animated">official bootstrap page</a> about it, there is no evidence of this behavior. (click on the "toggle animation" button to see it in action).</p>
<p>Well, apart from the fact that we had this 25 progress bars moving on the page, we really didn't have any clue of what was going on. Our browser is doing a lot of work to animate things smoothly, and as I said <em>bootstrap documentation does never mention this high CPU usage</em>. But did we really need it? of course not, so we took away the <code>active</code> class and leaved a striped, but static progress bar.</p>
<p><strong>Beware of all transitions that you have on your page, they consume more CPU than you might think;</strong> and if a computer is slightly outdated (who said "cheap phones"?) your customers might experience bad performance.</p>
Quando assumere Generalisti vs Specialisti
2015-02-19T13:00:32Z
https://michelenasti.com/2015/02/quando-assumere-generalisti-vs-specialisti/
<p>Oggi voglio riproporre in Italiano un articolo di una persona molto skillata sulle tecnologie web, Nicolas Zakas. Zakas è autore di ESLint e ha scritto <a href="http://amzn.to/2mKc6gl">questo libro su Javascript</a> che ho letto per metà e che mi ha dato le basi per capire Javascript dal di dentro, ossia tutte le sue parti più nascoste come scoping, prototyping, type system, object model, etc.</p>
<p>L'articolo che però vi propongo non è un articolo sul web e le sue tecnologie, bensì sul processo di assunzione nelle varie aziende. Ciò che si chiede Zakas è <a href="http://www.nczonline.net/blog/2014/07/15/generalists-and-specialists-thoughts-on-hiring/">quando assumere "<strong>generalisti</strong>" vs quando (e se) assumere "<strong>specialisti</strong>"</a>. Se capite l'inglese e avete dieci minuti leggetelo, ne vale la pena. Altrimenti, leggete qui sotto 🙂 Ne farò un riassunto e infine scriverò alcune considerazioni personali.</p>
<p>Ritengo che <em>questo tipo di ragionamento non si applica solo al mondo del software ma a tutti i livelli del mondo lavorativo</em>, quindi se leggendo questo articolo volete fare un paragone con la vostra situazione attuale, i commenti sono vostri amici 🙂</p>
<h2>Chi sono i "Generalisti"</h2>
<p>i Generalisti sono coloro che sono bravi a fare un po' di tutto, senza una reale specializzazione. Sono solitamente bravi a fare le cosiddette attività <em>server-side</em>: database, setup di tool complessi, builds, etc.</p>
<p>Riescono a passare da un linguaggio ad un altro con facilità, perché ciò che conta è il procedimento e non la tecnica. Queste persone riescono a portare a termine qualsiasi compito gli venga assegnato.</p>
<p>I Generalisti tendono a fare un lavoro buono, ma non eccezionale; spesso vanno in crisi quando bisogna ragionare su un paradigma di lavoro che richiede un modo di pensare diverso (es. mobile, frontend).</p>
<p>All'inizio del ciclo di vita di un'azienda (o di un progetto), spesso vengono assunte persone che sono capaci di fare compiti anche molto diversi da quelli inizialmente previsti. In questo caso i Generalisti sono la scelta migliore perché possono lavorare a più ambiti senza sentirsi in difficoltà. Inoltre all'inizio del ciclo di vita di un'azienda il sistema è relativamente piccolo e ogni programmatore deve conoscere ogni aspetto del progetto. Se tutti sanno fare tutto, questo lavoro viene notevolmente semplificato.</p>
<h2>Gli "Specialisti"</h2>
<p>Non c'è bisogno di me o di Zakas per spiegare chi sono gli specialisti 🙂 Sono programmatori che, spinti dalla passione e dall'esperienza, eccellono in alcuni campi (web e mobile, lì si trovano i più accaniti specialisti). Spesso hanno una conoscenza superiore alla media su come si implementano particolari soluzioni, a discapito di altre aree che magari hanno dimenticato, più o meno intenzionalmente.</p>
<p>All'inizio del ciclo di vita di un'azienda i Generalisti implementano molte soluzioni che sono "abbastanza buone"; non appena la crescita dell'azienda è diventata consistente, e ci si rende conto che le soluzioni "abbastanza buone" non sono più così buone, ed è il momento di assumere degli specialisti. Ma come?</p>
<p><img src="https://michelenasti.com/images/SPECIALIST-vs-GENERALIST.jpg" alt="specialisti vs generalisti" /></p>
<h2>Come assumere specialisti</h2>
<p>Generalmente è il front-end il problema principale dei generalisti; spesso non riescono a trasformare in realtà ciò che i designer hanno prodotto, e i manager iniziano a brontolare perché "<em>front-end is a feature</em>", e quindi si inizia a chiedere di qualcuno che sappia cosa fare. Tuttavia, <strong>non sempre le aziende sanno come assumere uno specialista</strong>. Il processo di assunzione è tarato sui Generalisti e le domande che vengono fatte in questi colloqui non vanno bene per gli Specialisti.</p>
<p>Un altro problema spesso paventato dai manager è che <strong>uno Specialista crea problemi di allocazione</strong>. Molti credono che uno Specialista, una volta esaurita la sua mole di lavoro, diventi un peso e non si sappia dove allocarlo.</p>
<p>Dunque il vero primo problema è riuscire ad assumere il primo specialista abbastanza bravo da risolvere problemi nel breve termine. Grazie a questa prima figura si potranno assumere altre persone nel medio termine, e fare un trasferimento di conoscenze verso gli altri nel lungo termine.</p>
<p>Facile no?</p>
<h2>Quando assumere uno specialista</h2>
<p>La risposta può variare, ma per sommi capi potremmo pensare a queste situazioni:</p>
<ul>
<li>quando le cose sono veramente messe male. Ossia quando il lavoro che era "abbastanza buono" ora è diventato un totale casino.</li>
<li>quando ci si vuole muovere su un'area completamente nuova e bisogna farlo in fretta (es. Quando si ha già una webApp e si deve creare subito una app mobile)</li>
</ul>
<h2>chi deve condurre l'intervista</h2>
<p>Se in azienda nessuno ha le competenze dello specialista che si vuole assumere, diventa un problema anche solo fargli un colloquio. Le solite domande non vanno bene per questo tipo di figure. Eppure in azienda qualcuno ha provato a svolgere il lavoro dello specialista, sporcandosi le mani con la UI o il mobile: secondo Zakas queste sono le persone che dovrebbero condurre l'intervista.</p>
<p>I contenuti dell'intervista dovrebbero vertere sui problemi già affrontati e da affrontare e vedere se lo specialista riesce a spiegare con facilità ciò che ha in mente per risolvere il problema. La capacità di spiegarsi è fondamentale quando si assume il primo specialista; egli infatti sarà colui che dovrà dialogare con tutti i Generalisti preesistenti.</p>
<p>A un certo punto della vita aziendale sarà difficile vivere senza Specialisti. Gli stessi Generalisti, quando il sistema sarà diventato troppo grande, inizieranno a focalizzarsi su parti del sistema e saranno loro stessi a chiedere di poter migliorare qualcosa. In genere questa è la fase in cui ci si trasforma da Generalisti a Specialisti.</p>
<p>Assumere Generalisti diventa addirittura un rischio in questa fase, in quanto uno sviluppatore prima di diventare produttivo può richiedere molto tempo. Zakas cita ad esempio una company per cui ha lavorato, in cui i programmatori ci mettevano 6 mesi per essere produttivi perché assumevano solo Generalisti e poi usavano tool proprietari, così da rendere impossibile il trovare qualcuno già skillato.</p>
<h2>l'approccio Google</h2>
<p>L'approccio utilizzato a Mountain View viene detto "<em>ultimate generalist</em>" : assumere solo le persone più brave, più in gamba, che sappiano ragionare. Google ragiona così perchè non sa queste persone su quale progetto andranno a lavorare: può darsi lavoreranno a migliorare un DB o a sviluppare App per iPhone, lo si scoprirà solo il primo giorno di lavoro.</p>
<p>È come se, dovendo fare una squadra di calcio, si punti a prendere i migliori giocatori disponibili sul mercato, ignorando completamente i ruoli: portiere, difensori, centrocapisti e attaccanti. Nel metodo Google, tutti possono trovarsi a fare tutto. Lo immaginate?</p>
<p>Per questo, assumere generalisti all'inizio è un'ottima idea, ma se si hanno grandi ambizioni (giocare in Serie A) a un certo punto bisogna assolutamente diversificare i ruoli specializzandoli. Assumere generalisti e puntare al <em>training on the job</em> diventa un problema di scalabilità, con molte persone nuove che non riescono a gestire la mole di lavoro subito.</p>
<h2>(inutili) Considerazioni personali</h2>
<p>Sebbene abbia poca esperienza lavorativa, ho visto questi comportamenti su piccola scala. Ho vissuto la fase in cui un'azienda è passata da tool proprietari a tool aperti, e quindi siamo riusciti a trovare nuovi specialisti subito. Ho anche visto quanto è sconveniente prendere persone alle prime esperienze e affidarsi al training on the job, specialmente quando il progetto è avviato da diversi anni.</p>
<p>L'approccio Google ha prodotto risultati interessanti e alterni: da un lato l'ideatore di GMail afferma di non aver avuto alcuna competenza su javascript quando ha iniziato a lavorarci, e insomma GMail era un esperimento per mettere in pratica ciò che stava imparando. Questo suo "sperimentare" lo ha trasformato da generalista a specialista; inoltre ha creato la prima webapp di successo che ha rivoluzionato il modo di pensare a javascript lato client.</p>
<p>Inoltre una grande parte del successo di Google dipende da ciò che accade lato server - i loro data center sono un esempio e sono copiati da centinaia di altre aziende nel mondo.</p>
<p>Sfortunatamente non tutti i generalisti hanno la fortuna di cambiare il mondo, anche se in questo campo Google domina sugli avversari (<a href="http://michelenasti.com/2015/02/pro-e-contro-di-angularjs-dopo-2-mesi-di-lavoro/">anche AngularJS è nato da un "generalista" a digiuno di javascript...</a>).</p>
<p>Dove falliscono i generalisti? Proprio lì dove gli altri invece hanno successo: guardiamo ad Apple, che su hardware non eccellente costruisce software perfettamente tarato ed integrato. Apple assume generalisti? Penso di no, anche se sulle procedure di assunzione della Apple c'è molta segretezza.</p>
<p>Io sono "nato" generalista, e sto facendo un grande sforzo per studiare il mondo web e frontend, di cui sono affascinato.</p>
<p>E voi? generalisti o specialisti?</p>
Scrivere codice migliore rispondendo a 12 semplici domande
2015-02-24T13:15:36Z
https://michelenasti.com/2015/02/scrivere-codice-migliore-rispondendo-a-12-semplici-domande/
<p>Qualcuno (per la precisione, uno dei creatori del celeberrimo Stack Overflow) nel 2000 ha ideato un <a href="http://www.joelonsoftware.com/articles/fog0000000043.html">questionario di 12 domande</a> per sapere se la vostra azienda (o il vostro progetto) sta seguendo le "buone pratiche di qualità del software", che tradotto in programmatorese, significa <strong>codice migliore</strong>. Questa roba non è fuffa, visto che esistono anche standard internazionali che codificano più o meno le stesse cose, solo che sono libri di 1500 pagine che non sono stati letti nemmeno da chi li ha scritti.</p>
<p>Ecco il famigerato questionario:</p>
<ol>
<li>Usate un sistema di source control?</li>
<li>Potete realizzare una build del sistema in un solo step?</li>
<li>Create una build giornaliera?</li>
<li>Avete un database dei bug?</li>
<li>Risolvete i bug prima di scrivere nuovo codice?</li>
<li>Avete una pianificazione aggiornata delle cose da fare?</li>
<li>Avete una specifica scritta di cosa devi fare?</li>
<li>I programmatori hanno condizioni lavorative tranquille?</li>
<li>Usate i migliori tool che potete permettervi?</li>
<li>Avete tester allocati sul progetto?</li>
<li>I candidati al ruolo di programmatore scrivono codice durante il colloquio?</li>
<li>Effettuate test di usabilità minimi?</li>
</ol>
<p>Il bello di questo test è che è facile rispondere; altri sistemi richiedono di calcolare il numero di bug per commit e cose simili, e non sono dunque rapidi. <em>Ovviamente non è un test da utilizzare per testare la sicurezza di una centrale nucleare</em> 🙂</p>
<p>Come si calcolano i punteggi? 12 è perfetto, 11 va bene, ma con 10 o meno c'è un serio problema in azienda.</p>
<p>Ci pensate? Eravamo nel 2000 quando è stato ideato questo questionario; le tecnologie cambiano, ma i processi di sviluppo no. Queste domande sono attualissime e probabilmente sono destinate a restarlo per molto tempo ancora, che si tratti di software desktop, webapp, o mobile.</p>
<h3>Usare un sistema di source control</h3>
<p>Che sia CVS, SVN o GIT, è impossibile gestire il lavoro di un team senza uno strumento del genere. L'ho fatto all'università (ai miei tempi c'erano ancora i floppy) e siamo impazziti.</p>
<h3>Realizzare una build in un solo step / una build giornaliera</h3>
<p>Se per compilare il vostro software c'è bisogno di più passi, allora il processo di build è passibile di errori dovuti a distrazione, cambio di configurazioni, problemi di environment. In un progetto su cui ho lavorato abbiamo ragionato per molti mesi per giungere a un sistema di build veloce e pratico che prende il codice da SVN, lo compila, lo invia via SSH alla macchina remota su cui deve essere installato, e avvia il server. Tutto ciò poteva essere lanciato on-demand e, comunque, ogni mezzanotte il processo veniva eseguito. (E praticamente tutte le mattine c'era un problema di compilazione da risolvere).</p>
<h3>Database dei bug / risolvere bug prima del nuovo codice</h3>
<p>Ho lavorato su un progetto in cui i bug venivano memorizzati in un bug-tracker, senza però essere gestiti. Poi quando il numero è salito a qualcosa come 10.000 ci siamo accorti di quanto fosse dispendioso, in termini di risorse, soltanto verificare se quei bug esistessero ancora. Spesso risalivano a funzionalità realizzate anni prima, e chi le aveva realizzate si era pure licenziato.</p>
<p>Risolvere i bug subito permette quindi di avere le persone giuste al momento giusto e allocate sulla cosa giusta; se ci si mette a risolvere i bug alla fine c'è il SERIO rischio che, per eliminare un singolo e semplice bug, si rompa un intero sistema. (cosa realmente accaduta).</p>
<h3>Pianificazione aggiornata / Specifica scritta</h3>
<p>Se lavorate con una metodologia Agile, come SCRUM, questo problema viene fuori subito: cosa dobbiamo fare per completare la storia assegnata?</p>
<p>Se invece si lavora senza una metodologia Agile, e mi è capitato, non si sa mai a che livello di specificità ci si deve fermare. Domande tipo "come implemento la recovery della password?" possono generare discussioni infinite o risposte stizzite. Attenti!</p>
<h3>Condizioni lavorative tranquille / tools migliori disponibili</h3>
<p>E' stato dimostrato che <a href="https://www.ironistic.com/the-cost-of-distractions-on-developers/">il principale ostacolo al lavoro dei programmatori sono le interferenze esterne</a>. Per quanto oggi si voglia spingere sul lavoro di gruppo, la programmazione è un lavoro che viene fatto da una sola persona davanti a un computer, che spesso lavora di notte per non essere disturbata dagli altri. E in effetti, le cuffiette sono indispensabili quando ho bisogno di concentrarmi su qualcosa. Spesso non metto neanche la musica, mi basta attutire i discorsi intorno a me.</p>
<p>Per quanto riguarda i tool: spesso è questione di preferenze personali. Anzi, pare che i migliori software in circolazione, salvo rare eccezioni, siano open source. Ma se un developer vuole testare una soluzione alternativa (o a pagamento), perché non provarla? non è detto che venga applicata subito, ma qualsiasi sperimentazione porta sempre a un arricchimento.</p>
<h3>Tester allocati sul progetto</h3>
<p>Ho lavorato per anni con dei tester allocati sul progetto, e portano innumerevoli benefici al progetto. Persone dedicate al testing che validano le specifiche che arrivano dall'analisi, diventano un surrogato della specifica parlante, che si interroga sui casi limite e aiuta a ragionare sui comportamenti ambigui. Molti problemi di specifica incompleta vengono beccati dai tester (se sono bravi) e soprattutto <em>non hanno scritto loro il codice che devono testare</em>.</p>
<p>Al contrario, lavorare senza tester è doloroso. Io scrivo codice perfetto, perché mai qualcuno dovrebbe testarlo? E se esce qualche bug, è sicuramente un caso limite che non dipende da me ma da uno degli 8 punti precedenti di questo articolo 🙂</p>
<h3>Candidati che scrivono codice</h3>
<p>L'idea alla base è: se assumi un cuoco, vorrai assaggiare i suoi piatti prima di assumerlo. Se assumi un mago, vorrai vedere qualche trucco di magia. Perché non fare lo stesso con gli sviluppatori?</p>
<p>Il principale problema di questo approccio è che, per uno sviluppatore che ha intenzione di affrontare il processo di selezione, dovrà sobbarcarsi di una certa mole di lavoro (8-10 ore di programmazione) che generalmente verranno svolte a casa, togliendo tempo ad altro. Quindi molti candidati si sentiranno scoraggiati ad affrontare un processo di selezione lungo e dall'esito incerto.</p>
<p>Io ho fatto più di una decina di colloqui di lavoro. Quasi mai ho scritto codice durante il processo di selezione, specialmente per realtà campane e Italiane. Ciò che conta davvero nelle aziende in cui mi è capitato di andare è l'esperienza accumulata, e quanto guadagnavo al momento. Qualcuno mi ha fatto domande teoriche (java, garbage collector, scrum, design patterns, testing...) ma le risposte erano quasi sempre concettuali. L'unico colloquio in cui ho scritto codice è stato per Amazon, ma il tizio a cui ho inviato i risultati ora non lavora più per loro. Potenza di LinkedIn.</p>
<h3>Test di usabilità minimi</h3>
<p>Cosa si intende per test di usabilità minimi? Sono quei test fatti prendendo 4-5 persone a caso (quelle che passeggiano dietro alla tua scrivania) chiedendogli cose tipo "prova a fare questo". Vedrete che poche persone troveranno il 90% dei problemi di usabilità a cui non avevate nemmeno pensato. Cose tipo "ma cosa dovrei cliccare?" oppure "non capisco cosa sta accadendo sul sistema in questo momento". Meglio averle subito queste risposte che in produzione.</p>
<h2>Quanti punti avete totalizzato?</h2>
<p>Nelle esperienze passate in media ho totalizzato intorno agli 8-9 punti. E voi?</p>
Tools that don't explain themselves clearly will not be used
2015-03-15T17:24:44Z
https://michelenasti.com/2015/03/tools-that-dont-explain-themselves-clearly-will-not-be-used/
<p>My life is full of stuff to do and I really have no time to update my knowledge, so I have to dig on rss feeds or conferences or ask friends for the latest tools to be aware of.</p>
<p>There are tons of tools that are super useful and also super easy to use. I won't cite here all of them; one of the best (that serves as an example for this article) is <a href="https://www.vagrantup.com/">Vagrant</a>: The first time I went on their website I understood clearly what was it's mission, and how they wanted to accomplish their goal. Not only, we were having this problem at work so Vagrant was a candidate solution.</p>
<p>Indeed, there are some websites that do not explain clearly what their tools want to do. I read a lot about it, and maybe I also get the idea behind it, but the right info is not coming from the official website.</p>
<p>I have two examples for two tools that everybody is talking about, and I have still not understood how they work (and what problem they try to solve). One of these is <a href="https://www.docker.com/">Docker</a>: every time I cite Vagrant as an example of an easy-to-use and clearly designed tool, people say "but there's also docker". Honestly I have not understood what docker does (maybe the same as vagrant?) and how (without virtual machines?). I read that there is a lot of hype about it, and also it seems that Docker has a lot of enemies and angry developers, but I cannot understand why.</p>
<p>Another tool that everybody is talking about is <a href="http://requirejs.org/">RequireJs</a> : In this case the website is pretty much theoretical and it's not doing a clear example on how to use it. Also, I have never found other tutorials or examples. There is a lot of hype, but the complexity of the tool has not been mitigated so we still have a lot of people using <code><script></code> tags everywhere. I would love to try it but I cannot figure out even how much time I need to migrate some existing project.</p>
<p>What do you think about this? Do you admit that there are some pieces of software that do not explain themselves clearly?</p>
<p>Also: is there anybody that can/wants to explain me this two tools? 😀</p>
Playing with my friend's 3D printer
2015-03-19T09:05:39Z
https://michelenasti.com/2015/03/playing-with-my-friends-3d-printer/
<p>Yesterday one of my best friends invited me to his house to see his new toy: a 3D printer, proudly bought on Internet from China. He assembled it piece from piece, and now he can print whatever he wants to.</p>
<p>I am not a great expert of 3D printers, however I'll try to explain some interesting features. Obviously if you have questions I'll ask my friend to write the answers down here.</p>
<p>But first, checkout this two little videos (audio in Italian, but images speak from themselves):</p>
<p><blockquote class="instagram-media" style="background: #FFF; border: 0; border-radius: 3px; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: calc(100% - 2px);" data-instgrm-captioned="" data-instgrm-version="4">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
</div></p>
<p><p style="margin: 8px 0 0 0; padding: 0 4px;">
<a style="color: #000; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" href="https://instagram.com/p/0YdTYnNSmX/" target="_top">A Montoro vedo in funzione la prima stampante 3d comprata per puro diletto !</a>
</p></p>
<p><p style="color: #c9c8cd; font-family: Arial,sans-serif; font-size: 14px; line-height: 17px; margin-bottom: 0; margin-top: 8px; overflow: hidden; padding: 8px 0 7px; text-align: center; text-overflow: ellipsis; white-space: nowrap;">
Un video pubblicato da @musikele in data: <time style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;" datetime="2015-03-18T20:17:20+00:00">18 Mar 2015 alle ore 13:17 PDT</time>
</p>
</div>
</blockquote></p>
<p><blockquote class="instagram-media" style="background: #FFF; border: 0; border-radius: 3px; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: calc(100% - 2px);" data-instgrm-captioned="" data-instgrm-version="4">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
</div></p>
<p><p>
<a style="color: #000; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" href="https://instagram.com/p/0YdfrItSm3/" target="_top">Vuoi stampare qualcosa in 3d? Chiedimi come! #NerdPorn</a> Un video pubblicato da @musikele in data: <time style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;" datetime="2015-03-18T20:19:01+00:00">18 Mar 2015 alle ore 13:19 PDT</time> </div> </blockquote></p>
<p><p>
</p></p>
<p><h3>
Some Technical Details
</h3></p>
<p><p>
This printer is extremely fast but it's one of the cheapest, so it is not so precise. It uses 13-15 passes to build 1 millimeter of plastic.
</p></p>
<p><p>
We can build objects with maximum dimensions of 200 x 200 x 180 millimeters.
</p></p>
<p><p>
The plastic used is PLA (very solid) or ABS (smoother), however ABS it is a bit toxic to breathe in house, so we are not experimenting with it for now.
</p></p>
<p><p>
Every software capable of designing CAD can be used, if it can export data in .STL format. (My friend says that this is very common among CAD software).
</p></p>
<p><p>
Yesterday we produced the small ball of the video in about 45 minutes.
</p></p>
<p><h3>
And now?
</h3></p>
<p><p>
My friend is willing to collaborate with anyone that wants to produce something; just talk to him because maybe your idea must be adjusted to fit in the printer.
</p></p>
<p><p>
It is not so common to have a friend with a 3D printer available: once you have it and see how easy it is to build your own things, it's like a drug: you never want to stop.
</p></p>
Howto: create a blog with Ruby on Rails
2015-03-24T13:30:53Z
https://michelenasti.com/2015/03/howto-create-a-blog-with-ruby-on-rails/
<p>I'm following a course on Coursera about Ruby on Rails, well the course is about <a href="https://www.coursera.org/course/webapplications" title="Web Application Architectures">Web Architectures</a> but it uses Ruby on Rails to explain it's uses. So this is a good excuse to experiment with this new language and framework.</p>
<p>It is not the first time that I try to learn RoR; when I completed my university it was in its "momentum" — it was one of the coolest frameworks to play with. Since 2012 things have changed a bit, now NodeJS is the new "new", but who cares: when you learn something new you should target at the idea, not the execution.</p>
<p>RoR is one of the first frameworks that have speed up web development; with some simple (very simple) commands you can build <del>ugly</del> websites that just work. Then you only have to customize it.</p>
<p>Some <em>advantages</em> of RoR: <strong>convention over configuration</strong>, so that you don't have to hassle with stupid details. Usually the defaults are already what you want. RoR is a good tool for <strong>Agile development</strong>, testing is built-in and the server doesn't need to restart if you modify something; a good set of <strong>generators</strong> that generate almost everything for you. Another principle worth of noting is <strong>DRY</strong> (<strong>don't repeat yourself</strong>), so for example you don't have to write the sql table, if you have the structure of the table written in the entity. You'll see this below.</p>
<p>And what about <em>disadvantages</em>? You have to learn a <strong>new language</strong> (Ruby), that outside RoR, is used less than never; there is some kind of **"magic" **in its generators, so you never know what's going on; and also Twitter, that has used Ruby in its early stage, has rewritten everything in Java: many have seen this as a problem of scalability. (It seems, however, that the scalability problems did not depend on Ruby itself but on a horizontal scalability issue).</p>
<h3>Let's get our hands dirty</h3>
<p>Assuming you have <a href="http://railsinstaller.org/en" title="Rails Installer">installed Ruby on Rails</a>, here are the steps to produce an "iteration 1" of a blog application:</p>
<pre class="language-shell"><code class="language-shell">rails new blog <span class="token operator">&&</span> <span class="token builtin class-name">cd</span> blog</code></pre>
<p>this command creates a new directory named <code>blog</code> and then we <code>cd</code> into it.</p>
<pre class="language-shell"><code class="language-shell">rails generate scaffold post title:string body:text<br /><br />rails generate scaffold comment post_id:integer body:text</code></pre>
<p>these two lines create the "<strong>post</strong>" and "<strong>comment</strong>" entities, as well as all the <strong>model</strong>, <strong>view</strong>, <strong>controllers</strong>, and <strong>tests</strong>. We specify the actual attributes of the entities on the command line; so for example post has a <code>title</code> of type string, and a <code>body</code> of type text. One note about <code>post_id</code> from the entity comment: it will be linked to the post, but in iteration 2.</p>
<pre class="language-shell"><code class="language-shell">rake db:migrate</code></pre>
<p><strong><code>rake</code></strong> is a command that will find changes in the model and will update the database accordingly; this follows the DRY principle (I told you!). It will also version your db scripts.</p>
<pre class="language-shell"><code class="language-shell">rake routes</code></pre>
<p>rake will create routes to the actual views. All this links are available to use.</p>
<pre class="language-shell"><code class="language-shell">rails server</code></pre>
<p>This command will start a server on <code>http://localhost:3000</code> . You should see a welcome page by ruby on rails. Can't see it ? Install it correctly !</p>
<p>If the server has started, you can then navigate to the routes outputted by <code>rake routes</code>, so try to go to <code>http://localhost:3000/posts</code> and <code>http://localhost:3000/comments</code>.</p>
<p>I want to point out that <strong>we didn't open any IDE ... just the console.</strong></p>
<p>What can we do with this? quite nothing, for now, but we are still in <strong>iteration 1</strong> !</p>
<p>Will we ever get a fully functional blog app? follow us on the next episode and you'll know!</p>
Codemotion Roma 2015: raccontiamo questa prima giornata
2015-03-27T21:25:17Z
https://michelenasti.com/2015/03/codemotion-roma-2015-raccontiamo-questa-prima-giornata/
<p>Anche quest'anno sono al Codemotion, anzi stavolta ho avuto anche la pazienza di fotografare un sacco di momenti e dunque ve li ripropongo così vi fate un'idea se è il caso di venire oppure no l'anno prossimo. Se volete la mia opinione, trattandosi della conferenza più famosa in Italia sulle professioni informatiche, non bisogna mancare!</p>
<p>Comunque, iniziamo così la giornata. Appena arrivati ci spariamo un selfie con lo sfondo di uno degli sponsor; perchè proprio loro? E perchè no, rispondiamo noi:</p>
<p><blockquote class="instagram-media" style="background: #FFF; border: 0; border-radius: 3px; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: calc(100% - 2px);" data-instgrm-captioned="" data-instgrm-version="4">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
</div></p>
<p><p style="margin: 8px 0 0 0; padding: 0 4px;">
<a style="color: #000; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" href="https://instagram.com/p/0uaKnvtShY/" target="_top">Live from Codemotion 2015!!!</a>
</p></p>
<p><p style="color: #c9c8cd; font-family: Arial,sans-serif; font-size: 14px; line-height: 17px; margin-bottom: 0; margin-top: 8px; overflow: hidden; padding: 8px 0 7px; text-align: center; text-overflow: ellipsis; white-space: nowrap;">
Una foto pubblicata da @musikele in data: <time style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;" datetime="2015-03-27T08:53:13+00:00">27 Mar 2015 alle ore 01:53 PDT</time>
</p>
</div>
</blockquote></p>
<p>Primo talk seguito al mattino con Svetlana Isakova che ci racconta il nuovo linguaggio per Android, Kotlin (che si rifà a Swift per molti aspetti):</p>
<p><blockquote class="instagram-media" style="background: #FFF; border: 0; border-radius: 3px; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: calc(100% - 2px);" data-instgrm-captioned="" data-instgrm-version="4">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
</div></p>
<p><p>
<a style="color: #000; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" href="https://instagram.com/p/0ub4G9tSie/" target="_top">Let's start with Svetlana from Jetbrains! She explains Kotlin, "the swift for Android"</a>
</p></p>
<p><p>
Una foto pubblicata da @musikele in data: <time style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;" datetime="2015-03-27T09:08:10+00:00">27 Mar 2015 alle ore 02:08 PDT</time> </div> </blockquote></p>
<p><p>
</p></p>
<p><p>
Appena uscito vado a vedere se sono arrivati quei pazzerelli di Mangatar, trovo solo lo stand montato, loro chissà dove sono (ho saputo poi che stavano facendo "colazione"):
</p></p>
<p><blockquote class="instagram-media" style="background: #FFF; border: 0; border-radius: 3px; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: calc(100% - 2px);" data-instgrm-captioned="" data-instgrm-version="4">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
</div></p>
<p><p style="margin: 8px 0 0 0; padding: 0 4px;">
<a style="color: #000; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" href="https://instagram.com/p/0ukVpptSqW/" target="_top">Friends will be friends 🙂</a>
</p></p>
<p><p style="color: #c9c8cd; font-family: Arial,sans-serif; font-size: 14px; line-height: 17px; margin-bottom: 0; margin-top: 8px; overflow: hidden; padding: 8px 0 7px; text-align: center; text-overflow: ellipsis; white-space: nowrap;">
Una foto pubblicata da @musikele in data: <time style="font-family: Arial,sans-serif; font-size: 14px; line-height: 17px;" datetime="2015-03-27T10:22:06+00:00">27 Mar 2015 alle ore 03:22 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
Altro talk, stavolta sulla gestione del tempo ad opera di Matteo Collina (nearForm). La cosa più curiosa è che questo tipo "telelavora", prospettiva che mi interesserebbe un bel po'! Nella slide, invidia per i koala.
</p></p>
<p><blockquote class="instagram-media" style="background: #FFF; border: 0; border-radius: 3px; box-shadow: 0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width: 658px; padding: 0; width: calc(100% - 2px);" data-instgrm-captioned="" data-instgrm-version="4">
<div style="padding: 8px;">
<div style="background: #F8F8F8; line-height: 0; margin-top: 40px; padding: 50% 0; text-align: center; width: 100%;">
</div></p>
<p><p style="margin: 8px 0 0 0; padding: 0 4px;">
<a style="color: #000; font-family: Arial,sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 17px; text-decoration: none; word-wrap: break-word;" href="https://instagram.com/p/0ul7zZtSsZ/" target="_top">"Invidio i koala</a>"
</p>
</div>
</blockquote></p>
<p><p>
Subito a seguire un talk su AngularJs, strumento che uso quotidianamente a lavoro. Gli speaker, Carlo Bonamico e Sonia Pini, sono delle vere e proprie autorità sull'argomento: ad ogni evento in cui si parli di Angular ci sono loro. (A proposito, quando parlate delle Promises?)
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0utfIwNSnQ/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Applicazioni reali con AngularJS con Carlo Bonamico e Sonia Pini</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T11:42:03+00:00">27 Mar 2015 alle ore 04:42 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
Stavolta becco davvero il team di Mangatar, sempre nella zona ristoro, ed ecco un bel selfie con i Dengen Chroniclers :
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0uzCsItSvg/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Il team di Mangatar! Indovinate chi manca (non si è voluto alzare dal divano)</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T12:30:36+00:00">27 Mar 2015 alle ore 05:30 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
A seguire un talk sulle Rest API da parte di un sito che le vende, ovvero Mashapi, ovvero Orlando K:
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0u2ma-NSlz/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Rest Api con @orliesaurus</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T13:01:41+00:00">27 Mar 2015 alle ore 06:01 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
Ecco poi uno dei talk che mi sono piaciuti più di tutti, un talk sulle differenze (e le affinità) di Scrum vs Kanban, ad opera di Carlo Breschi:
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0u8xF_tSvq/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Kanban Vs Scrum? Con Carlo Beschi</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T13:55:34+00:00">27 Mar 2015 alle ore 06:55 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
Io e Mauro proviamo una sorta di "Oculus", e facciamo un po' di evoluzioni volanti nella realtà virtuale:
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0vGnFENSgo/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Mauro prova La realtà virtuale di Brogrammers</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T15:21:35+00:00">27 Mar 2015 alle ore 08:21 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0vHCqPNShl/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Si vola coi Brogrammers!</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T15:25:21+00:00">27 Mar 2015 alle ore 08:25 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
Nel pomeriggio io e Mauro siamo passati davanti a un monitor e sopra c'erano una sorta di Quiz Nerd. Uno di questi ci ha preso molto e abbiamo passato una buona oretta tentando di risolverlo. Purtroppo l'hanno risolto prima di noi 🙁 Il secondo l'abbiamo risolto subito, ma il tizio che doveva dirci se la risposta era esatta era scomparsa 🙁
</p></p>
<p><p>
Ad ogni modo siamo andati a vedere un altro talk che non era proprio dedicato a noi, ma al mondo del project management; si parlava di applicare le metodologie Agile al mondo del Management:
</p></p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0vPy7YNSh7/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Agile Project Mangement: perché Agile Conviene?</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-27T16:41:51+00:00">27 Mar 2015 alle ore 09:41 PDT</time>
</p>
</div>
</blockquote></p>
<p><p>
</p></p>
<p><p>
A seguire grande birra sociale, relax su poltrone comodissime, e ora attendiamo con impazienza la giornata di domani!
</p></p>
Installing Ruby On Rails on Mac 10.10 is a pain
2015-03-29T22:13:30Z
https://michelenasti.com/2015/03/installing-ruby-on-rails-on-mac-10-10-is-a-pain/
<p>I've tried many "howto install Ruby" and "how to install Rails", at the end the only installation method that works on Mac 10.10 (Yosemite) is this: <a href="https://gorails.com/setup/osx/10.10-yosemite">Setup Ruby On Rails on Mac OS X 10.10 Yosemite</a></p>
<p>What to say? Installation went "ok" on Windows (this is not very true since I actually have Windows at work, and at work I have a proxy, so ... I had to play a little bit), but on my Mac I have found something like 3000 tutorials on how to install this and that, and no tutorial was similar to others. Thanks to the author!</p>
<p>Then I had a lot of warnings that I'm trying to fix, and mostly I go on Stack Overflow to understand what's happening. One of this errors is:</p>
<p><pre class="lang:sh decode:true">/usr/local/rvm/gems/ruby-1.9.3-p194@global/gems/bundler-1.9.1/lib/bundler/shared_helpers.rb:83:
warning: Insecure world writable dir /usr/local in PATH, mode 040777</pre></p>
<p>For what I understood, you have this problem because anyone can write in /usr/local . So the fix was to digit the following command:</p>
<p><pre class="lang:sh decode:true">chmod go-w /usr/local</pre></p>
<p><strong>You can also have the same error referred to other directories, just launch this command with the appropriate path.</strong></p>
<p>And now I'm having this other problem, unfortunately Stack Overflow does not have any ideas on how to solve it:</p>
<p><pre class="lang:default decode:true">/usr/local/rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/values/time_zone.rb:270:
warning: circular argument reference - now</pre></p>
<p>Can anybody help me?</p>
Codemotion Roma 2015 - day two
2015-03-30T08:14:53Z
https://michelenasti.com/2015/03/codemotion-roma-2015-day-two/
<p>Riprendiamo a parlare del Codemotion Roma, con qualche giorno di ritardo.</p>
<p>Red Hat distribuiva Fedora a tutti, o meglio a chi aveva la prontezza di riflessi di prendere un cappello:</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xHQg5tSlD/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Red Hat distribuisce Fedora per tutti al #Codemotion !</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T10:05:43+00:00">28 Mar 2015 alle ore 03:05 PDT</time>
</p>
</div>
</blockquote></p>
<p>A seguire sono andato a sentire un talk su "AbsurdJS", un framework che permette di scrivere HTML e CSS in Javascript:</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xSWowNSk0/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">AbsurdJS, un modo per scrivere css e html in javascript. Con Krasimir Tsonev #Codemotion</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T11:42:41+00:00">28 Mar 2015 alle ore 04:42 PDT</time>
</p>
</div>
</blockquote></p>
<p>Uno dei talk più belli in assoluto è quello sull'architettura di Stack Overflow, in cui abbiamo praticamente capito che per realizzare un sito di successo bisogna mettere in discussione TUTTO quello che sappiamo; sicuramente nei prossimi giorni scriverò due righe su questo talk veramente interessante.</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xSW_RtSk2/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Architettura di Stack Overflow al #Codemotion</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T11:42:44+00:00">28 Mar 2015 alle ore 04:42 PDT</time>
</p>
</div>
</blockquote></p>
<p>E' tempo di gadget! Abbiamo partecipato a un contest di Google (uno di quelli in cui bisogna programmare per trovare la soluzione) e abbiamo vinto due porta-bibite by Google! Anche in questo caso, a giorni, vi parlerò del problema affrontato!</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xSXGdNSk3/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Si vincono ricchi premi da #Google al #Codemotion!</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T11:42:45+00:00">28 Mar 2015 alle ore 04:42 PDT</time>
</p>
</div>
</blockquote></p>
<p>Un altro talk che abbiamo seguito è stato sul Lean Frontend Development:</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xeIjHtSrq/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Lean Frontend Development, con Matteo Guidotto al #Codemotion</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T13:25:37+00:00">28 Mar 2015 alle ore 06:25 PDT</time>
</p>
</div>
</blockquote></p>
<p>Marco Fusco, direttamente da Red Hat, ci parla di RxJava, una libreria che permette di consumare gli oggetti di un flusso man mano che arrivano. (Ci ha fatto pariare perchè ci ricordava un nostro amico Mondragonese!)</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xjE4-NSio/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Con Mario Fusco di Red Hat che ci parla di RxJava! #Codemotion</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T14:08:48+00:00">28 Mar 2015 alle ore 07:08 PDT</time>
</p>
</div>
</blockquote></p>
<p>Un'altra bella sorpresa di questo Codemotion: OrientDB, un database scritto da un team quasi tutto italiano, con un sacco di funzionalità e soprattutto dotato di une bella funzionalità: le live query. Ne parleremo in un articolo sicuramente!</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xsS8xNSvV/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Reactive Programming with nodejs, socket.io & orientdb</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T15:29:22+00:00">28 Mar 2015 alle ore 08:29 PDT</time>
</p>
</div>
</blockquote></p>
<p>Una foto con Enrico del team di Mangatar sfuggito alla foto del giorno prima:</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0xyg94tSuW/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Sembra l'orsetto di winner taco... Coi capelli fatti</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T16:23:43+00:00">28 Mar 2015 alle ore 09:23 PDT</time>
</p>
</div>
</blockquote></p>
<p>Siamo in dirittura di arrivo. Raffaele di Mangatar aspetta di ricevere il premio "best gaming startup" del Codemotion, e noi pensiamo che sia uno scherzo...</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0x5Vd8tSud/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Girano strane voci su quel signorotto al centro della foto #Codemotion #mangatar #dengenchronicles</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Una foto pubblicata da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T17:23:18+00:00">28 Mar 2015 alle ore 10:23 PDT</time>
</p>
</div>
</blockquote></p>
<p>... Invece hanno vinto davvero!</p>
<p><blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="4" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);">
<div style="padding:8px;">
<div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50% 0; text-align:center; width:100%;">
<div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;">
</div>
</div></p>
<p><p style=" margin:8px 0 0 0; padding:0 4px;">
<a href="https://instagram.com/p/0x6L8BNSgC/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_top">Ebbene ha vinto Mangatar! #Macomecaz #Codemotion #vivogliobene #mangatar</a>
</p></p>
<p><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">
Un video pubblicato da @musikele in data: <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-03-28T17:30:45+00:00">28 Mar 2015 alle ore 10:30 PDT</time>
</p>
</div>
</blockquote></p>
<p>E così si conclude la giornata! Corsa in metro fino a Tiburtina per prendere il treno che ci ha riportati a casa. E non abbiamo nemmeno vinto il premio a sorteggio, peccato!</p>
BlogZinga!
2015-04-08T21:49:22Z
https://michelenasti.com/2015/04/blogzinga/
<p><a href="http://www.blogzinga.it/#!/home">Sono su BlogZinga</a>!</p>
<p>Se avete un blog sull'IT, e siete italiani, fate application! BlogZinga è "una raccolta di blog sulla programmazione scritti da sviluppatori italiani." Il modo per farsi aggiungere è il più nerd possibile: con una git pull request 🙂</p>
<p>E' il momento delle feature request: cari amici di BlogZinga, perchè non scorrersi anche i feed e riproporre degli excerpt degli articoli ...</p>
IntelliJ IDEA, alla fine è arrivato l'amore
2015-04-09T13:30:21Z
https://michelenasti.com/2015/04/intellij-idea-alla-fine-e-arrivato-lamore/
<p>Alla fine è scoccato l'amore.</p>
<p>A lavoro non ne potevo più di Eclipse che si bloccava per ogni dieci minuti, anche per le operazioni più comuni. Certe volte bastava ridurlo a icona e Windows 8 lo swappava su disco, e ci mettevo almeno 10 minuti per riprendere possesso del PC. Per non parlare degli enormi quantitativi di RAM buttati al vento.</p>
<p>Ho provato <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a> perchè già avevo provato <a href="https://www.jetbrains.com/webstorm/">WebStorm</a>, e come editor web/javascript l'ho trovato il top. Così ho pensato, dato che a lavoro usiamo JavaEE sul backend e AngularJS/Javascript sul frontend, forse IntelliJ può essere la soluzione!</p>
<p>Ecco alcune cose che mi hanno sorpreso piacevolmente, in ordine sparso:</p>
<ul>
<li>possibilità di impostare il keymap di Eclipse, così non bisogna imparare tutte le combinazioni di tasti da zero. Non tutte sono mappate, ma per fortuna nei menù sono chiaramente elencate. Quella che mi manca di più è Ctrl+1.</li>
<li>Riconosce da solo i bean di Spring e se un bean non viene trovato non vedi il simboletto verde di fianco al nome! La cosa non è banale visto che noi abbiamo una configurazione di Spring e di Maven completamente fuori standard.</li>
<li>IntelliJ ha capito da solo che i nostri progetti erano "mavenizzati" e mi ha fornito una bella scheda per gestire tutti i task. (piccolo bug: il tasto "skip tests" non funziona).</li>
<li>La gestione di SVN (o meglio <a href="http://en.wikipedia.org/wiki/Revision_control">VCS</a>) è completamente diversa rispetto ad Eclipse; infatti ho dovuto fare diversi esperimenti prima di capire esattamente come fare per aggiornarmi, mergiare (si può dire?) e committare. Alla fine trovo quella di IntelliJ più intuitiva (e soprattutto più veloce).</li>
<li>Integrare <a href="http://zeroturnaround.com/software/jrebel/">JRebel</a> è costato il tempo di un click (install il plugin, fatto).</li>
<li>Posso debuggare contemporaneamente Java e Javascript dall'IDE! Finalmente non devo impazzire con la Chrome Developer Tools, che rimane uno strumento indispensabile per capire cosa sta succedendo, ma la comodità di capire (e modificare) il codice da un solo IDE è impagabile.</li>
<li>trovo che il modo di gestire le varie impostazioni dell'IDE sia molto più logico e ordinato rispetto a Eclipse.</li>
<li>Ancora su SVN: purtroppo qualcuno del mio team ha committato i .project e i .classpath ; se li cancellassimo da SVN succederebbe un bel casotto, dato che al successivo update eclipse cancellerebbe questi file e impazzirebbe. Con intelliJ ho spostato tutti questi file in una lista di commit separata (chiamata "non committare!") così non mi compaiono più nell'elenco dei file in uscita.</li>
<li>L'integrazione con il workspace di Eclipse è stata fantastica. Addirittura sto lavorando sui file già scaricati da Eclipse quindi se ho qualche problema e devo tornare al vecchio IDE vedo tutti i file aggiornati. Addirittura tutta la configurazione di SVN, Maven, etc l'ha presa dai settings di Eclipse. Più facile di così... !</li>
</ul>
<p>Ci sono anche alcune cose che ho dovuto scoprire da me, o che ho dovuto impostare io perchè il setting out-of-the-box non era proprio l'ideale.</p>
<ul>
<li>Impostare Tomcat è stato un casino. Avevo sempre un errore che IntelliJ non riusciva a connettersi al server, che partiva, però non riusciva a deployare l'applicazione. Alla fine ho risolto, e <a href="http://stackoverflow.com/questions/25147843/server-is-not-connected-when-trying-to-deploy-with-intellij/29509240#29509240">ho lasciato un commento su SO con la mia soluzione</a>.</li>
<li>Impostare il debug di Javascript in IntelliJ è stato un altro casino. Non ne avevo la priorità, però visto che l'ambiente lo permetteva, dovevo farlo. La cosa importante da capire è che bisogna inserire ESATTAMENTE l'indirizzo della pagina che verrà caricata all'avvio del server. Ad esempio, se l'indirizzo che digitate voi è <code>http://localhost:8080/WebApp</code> , ma poi c'è un redirect a <code>http://localhost:8080/WebApp**/**_#/authentication_</code> , bisogna inserire il secondo indirizzo, altrimenti chrome si blocca e sembra che non stia facendo assolutamente nulla.</li>
<li>La documentazione di IntelliJ non fa capire molto bene queste cose, trascura un po' di dettagli.</li>
<li>E poi... perchè nei log del server non c'è lo <strong>scroll automatico</strong>?!!?!? che rabbia!!</li>
</ul>
The Blog App in Ruby on Rails - iteration 2
2015-04-13T20:30:12Z
https://michelenasti.com/2015/04/the-blog-app-in-ruby-on-rails-iteration-2/
<p>Previous Articles and tutorials about Ruby:</p>
<ol>
<li><a href="http://michelenasti.com/2015/03/howto-create-a-blog-with-ruby-on-rails/">Howto: create a blog using Ruby on Rails</a></li>
<li><a href="http://michelenasti.com/2015/03/installing-ruby-on-rails-on-mac-10-10-is-a-pain/">Installing Ruby on Rails on Mac is a pain</a></li>
</ol>
<p>In the <a href="http://michelenasti.com/2015/03/howto-create-a-blog-with-ruby-on-rails/">previous article</a> we created a <code>post</code> entity and a <code>comment</code> entity. If you remember, we did not create the one-to-many relationship between these two objects of the model. Let's see how to create them.</p>
<p>In real world applications there are three types of relations:</p>
<ul>
<li><strong>one-to-one</strong></li>
<li><strong>one-to-many</strong></li>
<li><strong>many-to-many</strong></li>
</ul>
<p>I have ~stolen~ acquired a photo on how to organize relations inside ruby objects:</p>
<p><img src="https://michelenasti.com/uploads/2015/04/rubyrelations.png" alt="Ruby on Rails relations" /></p>
<p>Let's take as example the <code>comment</code> entity. It has a <code>post_id</code> attribute that obviously will contain the Id of the <code>post</code> linked. So, as we see in the previous table, our relation is of type Many-To-One (since many comments can belong to only one post). Comment is the model with the foreign key so we will add this line to the <code>Comment.rb</code> class:</p>
<pre class="language-ruby"><code class="language-ruby">belongs_to <span class="token symbol">:post</span></code></pre>
<p>While in the <code>post</code> class we will write</p>
<pre class="language-ruby"><code class="language-ruby">has_many <span class="token symbol">:comments</span></code></pre>
<p>Note that Ruby will handle by itself the pluralism of the entity, that's why we write <code>:comments</code> and not <code>:comment</code>.</p>
<p>Another modification that we can do is to say that, if we delete a post, we want to delete all the relative comments too. We can achieve this by writing</p>
<pre class="language-ruby"><code class="language-ruby">has_many <span class="token symbol">:comments</span><span class="token punctuation">,</span> <span class="token symbol">dependent</span><span class="token operator">:</span> <span class="token symbol">:destroy</span></code></pre>
<p>As of now we have linked the two models of our application.</p>
<p>How to quickly test it? We can open the Rails Console by executing</p>
<pre class="language-shell"><code class="language-shell">rails console</code></pre>
<p>In a terminal.</p>
<p>Here we have a full Ruby console but with all Rails classes preloaded, so we can work on the entities and explore a little bit.</p>
<p>For example, let's write:</p>
<pre class="language-ruby"><code class="language-ruby">Comments<span class="token punctuation">.</span>all</code></pre>
<p>To get all the comments in the database (if you don't have any comments start your rails application and write some comments at <code>http://localhost:3000/comments</code>).</p>
<p>Let's write</p>
<pre class="language-ruby"><code class="language-ruby">Posts<span class="token punctuation">.</span>all</code></pre>
<p>To get a reference of all posts. Now we can treat all posts like an array, so If we write</p>
<pre class="language-ruby"><code class="language-ruby">p<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>destroy</code></pre>
<p>You will see that the <strong>second</strong> post will be deleted (you know that we start counting from zero, don't you?). And since we have used the <code>:destroy</code> option, we will see also that all relative comments will be deleted too.</p>
<p>Check with <code>Comments.all</code>.</p>
<p>Let's digit quit to exit the console and let's have a look at the routes, writing <code>rake routes</code>.</p>
<p>What we see now is routes for all the comments and routes for all the posts. But the ideal would be to have comments under the Posts route (something like <code>/posts/1/comments</code>).</p>
<p>How to do this?</p>
<p>Let's open the <code>/config/routes.rb</code> file. All the routes are shown just using the commands <code>resource :posts</code> and <code>resources :comments</code>. If we want to show the comments route under the posts, we have to write:</p>
<pre class="language-ruby"><code class="language-ruby">resources <span class="token symbol">:posts</span> <span class="token keyword">do</span><br /> resources <span class="token symbol">:comments</span><br /><span class="token keyword">end</span></code></pre>
<p>Launch again <code>rake routes</code> in the console and now you'll see new routes, exactly as we wanted.</p>
<p>If we start the Webserver (Rails server) we can try these new urls, however they actually break something in the view, something that we will fix soon.</p>
<p>In the next lesson we will talk about validation. This is a good moment to save our work!</p>
How would you call your bleeding-edge Stuff framework in all programming languages and platforms?
2015-04-14T17:22:00Z
https://michelenasti.com/2015/04/how-would-you-call-your-bleeding-edge-stuff-framework-in-all-programming-languages-and-platforms/
<p>Javascript: <strong>StuffJS</strong></p>
<p>Java: <strong>JStuff</strong></p>
<p>.NET: <strong>NStuff</strong></p>
<p>Node: <strong>Stuff.IO</strong></p>
<p>C: <strong>stuff-c</strong></p>
<p>Angular: <strong>ng-stuff</strong></p>
<p>Python: <strong>PyStuff</strong></p>
<p>Apple: <strong>iStuff</strong></p>
<p>Php: <strong>PhpMyStuff</strong></p>
<p>Ubuntu: <strong>StuffBuntu</strong></p>
<p>KDE: <strong>Kstuff</strong></p>
<p>Android: <strong>StuffDroid</strong></p>
<p>Windows phone: it won't be named, because it won't be used, but if would be the case it would be <strong>WPStuff</strong></p>
<p>Wordpress: <strong>WP-Stuff</strong> (there's a hypen!)</p>
<p>CSS: <strong>StuffSS</strong></p>
<p>Arduino: <strong>Stuffuino</strong></p>
<p>RaspberryPI: <strong>StuffPI</strong></p>
<p>and finally, if it were a startup in Naples**: NAStuff**</p>
PHP Symphony: che faccio, me lo studio?
2015-04-17T13:45:46Z
https://michelenasti.com/2015/04/should-i-learn-php-symphony/
<p>Studiare o non studiare <a href="http://symfony.com/">Symphony</a>?</p>
<p>Da qualche mese sto provando a studiare Ruby, e ammetto che è molto difficile beccare tutti i concetti interessanti quando non stai sviluppando <em>attivamente</em> un progetto ma lo fai solo nel tempo libero. Però le cose che sto imparando sono carine e valevoli. Ora un amico mi ha proposto di aiutarlo nella realizzazione di una sua idea, che lui ha già iniziato con Php e Symphony.</p>
<p>Per chi non lo sapesse, tra cui anche io, Symphony è un po' la <em>summa</em> di tutte innovazioni nei vari framework più recenti: ha diverse feature, tra cui ORM, Dependency Injection, un'architettura ben definita, tools per lo unit testing, routing per le pagine web, e un motore di templating.</p>
<p>(Come potete vedere le idee le hanno prese da Hibernate, Spring, Play Framework, Ruby, JUnit, Django...)</p>
<p>Inoltre quelli di Symphony hanno realizzato alcune soluzioni interessanti, come la developer toolbar, per cui quando si sta sviluppando l'app in modalità debug si hanno tutta una serie di info sempre a portata di mano.</p>
<p>So che PHP ha ancora un grandissimo market share. Però mi sembra un po' retrò. Però non posso ignorare che la stragrande maggioranza di web tools attualmente sul mercato, come WordPress, Magento, WooCommerce, Joomla, Drupal sono realizzati in PHP. Insomma... E' forse il momento di "aggiornare le proprie conoscenze" al 2015 ?</p>
<p>Fatemi sapere se volete le vostre esperienze.</p>
due risate con la libreria JS del giorno: Elevator.JS
2015-04-24T09:41:54Z
https://michelenasti.com/2015/04/la-libreria-js-del-giorno-elevator-js/
<p>Non voglio perdermi in chiacchiere: alzate il volume e, nel caso, mettete le cuffie.</p>
<p><a href="http://tholman.com/elevator.js/">Elevator.JS</a></p>
<p>WOW.</p>
Coderdojo a Salerno: la mia esperienza
2015-05-04T18:30:41Z
https://michelenasti.com/2015/05/coderdojo-a-salerno-la-mia-esperienza/
<p>Qualche giorno fa è stato organizzato un secondo appuntamento del <a href="http://www.coderdojoitalia.org/">CoderDojo</a> a Salerno, presso la sede di Puntolingue sul primo binario della stazione FS.</p>
<p>Cos'è il CoderDojo? E' un evento dedicato ai ragazzi dai 10 ai 13 anni in cui si insegna a programmare, grazie ad un linguaggio di programmazione completamente visuale chiamato <a href="https://scratch.mit.edu/">Scratch</a>.</p>
<p>Ieri l'amico Raf (@duplikey) ha tenuto lui la "lezione" e io ho provato a fare da aiutante: il mio ruolo è stato di aiutare i ragazzini che sono rimasti indietro, o che avevano un dubbio, o che volevano andare "oltre".</p>
<p>Due parole sul tool: Scratch è un linguaggio di programmazione completamente visuale con cui é facilissimo realizzare videogiochi 2D. Le istruzioni sono blocchi di codice che si spostano col mouse per comporre programmi simili a diagrammi di flusso.</p>
<p>E così ieri abbiamo realizzato, tra tanti altri esempi, il simpatico giochino del gatto e del topo. Quando il gatto catturava il topolino, che si muoveva in maniera casuale, il punteggio si incrementava e il topolino scompariva per riapparire da qualche altra parte.</p>
<p>Esempi di domande che mi hanno fatto:</p>
<ul>
<li>
<p>"io premo Freccia Su ma il gatto va a destra"</p>
</li>
<li>
<p>"come faccio a far scomparire l'aereo in un buco nero?"</p>
</li>
<li>
<p>"la palla quando tocca la barra l'attraversa al posto di rimbalzare.."</p>
</li>
</ul>
<p>-" come azzero il punteggio se ricomincio la partita? "</p>
<p>Insomma i ragazzi ci vanno dentro, dopo un po' prendono iniziativa e tirano fuori di tutto. C'è chi ha realizzato (o vuole realizzare) una versione multi-player di pong, chi invece ha realizzato il gioco del gatto e del topo (uno dei nostri esempi) con 40 topi e 15 gatti (tweakando il programma). Poca giocabilità ma massimo divertimento!</p>
<p>Insomma sono rimasto colpito dal fatto che in sole 2 ore i ragazzi, seppur guidati, hanno realizzato almeno un videogiochino completo. Dispiace solo per un ragazzo che chiedeva di fare giochi di macchine per il padre (e per la playstation). Vaglielo a spiegare che si arriva per gradi a certe cose... Dovrà partecipare a qualche CoderDojo in più per raggiungere i suoi obiettivi 😉</p>
convertire label in camelCase o CONSTANT_CASE con IntelliJ e String Manipulation
2015-05-05T18:45:08Z
https://michelenasti.com/2015/05/convertire-label-in-camelcase-o-constant_case-con-intellij-e-string-manipulation/
<p>Spesso ci impelaghiamo in task pallosi e manuali (ma purtroppo necessari) per le azioni più disparate; penso a quando bisogna bonificare dei dati per il db (e quindi rimuovere caratteri non accettati) oppure formattarli in un certo modo. Bene, per tutto questo c'è il plugin di IntelliJ String Manipulation!</p>
<p>Premetto che esistono centinaia di migliaia di Tool che fanno quello che sto per descrivere; io tuttavia uso IntelliJ quindi riuscire a risolvere tramite l'IDE gli fa acquistare centinaia di punti stima!</p>
<p>Per chi se lo fosse perso, ecco un articolo su <a href="http://michelenasti.com/2015/04/intellij-idea-alla-fine-e-arrivato-lamore/">cosa mi piace di IntelliJ</a>.</p>
<p>Problema: devo convertire una lista di stringhe come</p>
<p><pre class="lang:default decode:true">Anagrafica
Sesso
Numero Civico Residenza
Comune Residenza
...</pre></p>
<p>in quest'altro formato:</p>
<p><pre class="lang:default decode:true">ANAGRAFICA
SESSO
NUMERO_CIVICO_RESIDENZA
COMUNE_RESIDENZA
</pre></p>
<p>Questo perchè poi devo tradurre le label tramite <a href="https://angular-translate.github.io/">angular-translate</a>.</p>
<p>Soluzione:</p>
<p>Installando il plugin di IntelliJ String Manipulation (qui il <a href="https://plugins.jetbrains.com/plugin/2162?pr=phpStorm">suo sito web</a>), è possibile selezionare queste label e cliccare ALT-SHIFT-M e poi J ... et voilà! il gioco è fatto.</p>
<p>Il plugin fa molto altro, permette di trasformare in mille altri modi il testo, oltre ad altre funzioni che per ora non ho esplorato.</p>
<p>E ora... siamo pronti ad affrontare anche i compiti più noiosi!</p>
BASTA! ai powerpoint fatti male
2015-05-06T18:32:43Z
https://michelenasti.com/2015/05/basta-ai-powerpoint-fatti-male/
<p>Questo articolo l'ho riadattato nel 2009 su un vecchio articolo di Paolo Attivissimo; ogni tanto qualche amico mi dice che deve preparare delle slide e io continuo a ripetere questi suggerimenti. Magari possono essere utili per chi deve ancora preparare le slide della tesi, o si sente insicuro. Seguite questi consigli: io l'ho fatto e ancora oggi ricordano le mie presentazioni all'università.</p>
<hr />
<p>Visto che in questa benedetta laurea specialistica ci stanno <em>abboffando</em> di progetti che consistono, spesso, in una relazione finale con tanto di <strong>presentazione <del><em>pauerpoint</em></del>powerpoint</strong>, ho trovato un bellissimo <em>elenco puntato</em> con i punti più importanti da tenere a mente quando si realizza una presentazione.</p>
<p>L’articolo originale parla del <a href="http://attivissimo.blogspot.com/2009/08/25-anni-di-powerpoint.html">programma PowerPoint</a>, che in origine era solo per Mac.</p>
<ul>
<li><strong>Non voltate le spalle al pubblico.</strong> Il pubblico è lì per ascoltare voi, per sentirvi parlare dal vivo, non per guardare le slide. Trovate la maniera di tenere sotto controllo cosa c’è realmente sullo schermo dietro di voi senza voltarvi e mantenete il più possibile il contatto visivo con il pubblico.</li>
<li><strong>Evitate fiumi di testo.</strong> Le slide non devono essere come pagine di libro. Se lo sono, la gente farà fatica a leggerle e a seguire contemporaneamente quello che state dicendo. Devono contenere poche parole essenziali: il discorso articolato dovete farlo voi.</li>
<li><strong>Non leggete le slide</strong>. Chi legge mentalmente va molto più veloce di chi legge ad alta voce, per cui ripetere pari pari il contenuto delle slide è mortalmente noioso.</li>
<li><strong>La presentazione non è la scaletta delle cose che dovete dire</strong>.Deve essere un complemento arricchente al vostro discorso: non deve essere lo schema del discorso. Quello va messo nelle note su schermo che il pubblico non vede, oppure su un foglio di carta.</li>
<li><strong>Evitate scritte microscopiche e grafici troppo intricati</strong>. E’ una presentazione, santo cielo, non un esame della vista. Il pubblico non riuscirà a cogliere tutti i dettagli, se sono minuscoli.</li>
<li><strong>Evitate schemi di colore troppo sgargianti</strong>, ma anche quelli banali. Il già citato testo giallo su sfondo blu è da evitare il più possibile, come lo sono i colori psichedelici. I modelli predefiniti dei programmi per presentazioni di solito sono un buon compromesso, ma bisogna ricordarsi di usarli e di variarli periodicamente. Tenete presente che le condizioni di luce di una proiezione sono sempre peggiori di quelle nelle quali guardate la presentazione sul vostro monitor.</li>
<li><strong>Usate immagini efficaci</strong>. Non ricorrete alla clipart insignificante e già vista: ricorrete a foto d’impatto, divertenti o simboliche, che aggiungano contenuto invece di complementare le parole. Se potete esprimere un concetto esclusivamente mediante un’immagine, fatelo.</li>
<li><strong>Spezzate le slide prolisse</strong>. Troppi concetti in una singola slide non verranno memorizzati. Suddivideteli su più slide.</li>
<li><strong>Preparate un’introduzione accattivante e una frase finale memorabile</strong>. Non siete lì per fornire a voce quello che potreste distribuire come stampato. Ogni presentazione è, a modo suo, uno spettacolo emozionale. Se non mostrate che quello di cui parlate vi appassiona, non potete pretendere di appassionare il pubblico.</li>
<li><strong>Provate, provate, provate</strong>. Fate passare e ripassare le slide per assicurarvi che le transizioni avvengano correttamente e che gli elementi di ogni slide appaiano nell’ordine giusto. Esercitatevi a fare la presentazione provandola ad alta voce per controllarne i contenuti e la durata.</li>
<li><strong>Controllate l’ortografia</strong>. Lasciare strafalcioni è il modo migliore per comunicare al pubblico che siete superficiali e disattenti.</li>
<li><strong>Non mostrate al pubblico il contatore del numero delle slide</strong>. Questo spinge lo spettatore a fare un conto alla rovescia mentale e a concentrarsi sul "quanto manca ancora" invece che sui contenuti. Non c’è niente come vedere "1 di 178″ per far scappare il pubblico o indurlo a simulare malori.</li>
<li><strong>Attenti alla cliccata fantasma</strong>. Alcune transizioni richiedono tempo per essere visualizzate. Se cliccate prima che siano finite, credendo che il computer non abbia "preso" la cliccata precedente per andare avanti, finirete nella slide successiva e vi perderete. Tenete d’occhio gli indicatori sul vostro schermo, che segnalano quando ogni transizione è stata completata.</li>
<li><strong>Memorizzate i tasti d’emergenza</strong>. Se scappa una cliccata fantasma, o volete saltare una slide o tornare indietro, segnate su un Post-it i tasti da usare. Vi servirà nei momenti di panico.</li>
<li><strong>Includete solo quello che vi serve per illustrare il concetto</strong>. Non rimpinzate la presentazione di dati irrilevanti.</li>
<li><strong>Provate le connessioni</strong>. Verificate in anticipo che il vostro computer sia compatibile con il videoproiettore e se possibile lasciatelo collegato e impostato. Assicuratevi che l’alimentatore del computer sia inserito e alimenti correttamente il computer, altrimenti la vostra presentazione verrà interrotta tragicamente dallo spegnimento del PC.</li>
<li><strong>Spegnete screensaver e risparmio energetico</strong>. Se parlate troppo a lungo su una slide, non volete che lo schermo dietro di voi diventi nero o, peggio ancora, faccia scorrere sul megaschermo la vostra collezione di foto porno.</li>
<li><strong>Preparate un backup; anzi due</strong>. Tenete una copia della presentazione su una penna USB e generatene una versione in formato PDF, da mettere anch’essa sulla penna. Il PDF funzionerà su qualsiasi computer d’emergenza. Stampate una copia della falsariga della vostra presentazione. La carta non crasha.</li>
<li><strong>Usate il vostro computer; se necessario, insistete</strong>. Specialmente se c’è poca luce in sala, rischiate di non trovare i tasti dove siete abituati a trovarli, e il software e il sistema operativo possono essere differenti e incompatibili. A volte basta una versione leggermente differente di software per rovinare l’impaginazione, ed è facilissimo che il computer altrui non abbia i vostri font prediletti.</li>
<li><strong>Non guardate la persona che sta dormendo</strong>. C’è sempre, anche se la vostra presentazione tratta di lingerie per pornostar, e se cominciate a fissarla perderete irrimediabilmente entusiasmo e concentrazione.</li>
</ul>
<p>Un grazie a <a href="http://www.attivissimo.net/">Paolo Attivissimo</a> e ai suoi articoli divulgativi. 10 anni fa mi ha portato sulla strada dell’informatica – e linux era ancora "robaccia" in bianco e nero.</p>
<p>Un giorno vi farò vedere le "mie" slide, fatte per i vari corsi & progetti. <em>Se mi va</em>. E voi che ne dite? Avete altri suggerimenti?</p>
Interpretare HTML in una stringa popolata da angular con ng-bind-html
2015-05-17T00:19:27Z
https://michelenasti.com/2015/05/interpretare-html-una-stringa-popolata-da-angular-con-ng-bind-html/
<p>Lo sapevate che non potevate inserire del testo html all'interno di una variabile _bind_ata con angular? Eh? Eh? Lo sapevate?</p>
<p>Facciamo l'esempio: in un controller dichiariamo una variabile così:</p>
<pre class="language-javascript"><code class="language-javascript">$scope<span class="token punctuation">.</span>errorMessage <span class="token operator">=</span> <span class="token string">'this is not good! <br>; it won\'t work!'</span></code></pre>
<p>e nell'HTML, a un certo punto, vogliamo mostrare questo messaggio in un div:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">ng-controller</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>pre</span><span class="token punctuation">></span></span></code></pre>
<p>il <code><br></code> sarà interpretato come "a capo"? o vedremo scritto proprio <code><br></code>?</p>
<p>....Vedremo scritto proprio <code><br></code>. Motivo per cui ci sono io a spiegarvi perchè e come aggirare il problema 🙂</p>
<p>Pochi sanno che quando scriviamo <code><p></p></code> in realtà per Angular stiamo scrivendo <code><p ng-bind="variabile"></p></code>; insomma il famosissimo <em>two way binding</em> di Angular è una direttiva bella e buona! I creatori di Angular hanno pensato che è decisamente più chiaro scrivere con le parentesi graffe, in modo che anche un non-programmatore possa capire il contenuto dell'html. Angular stesso poi tradurrà le <code>{{...}}</code> in <code>ng-bind="..."</code>, e infine la variabile in testo.</p>
<h3>Un buon motivo per usare ng-bind</h3>
<p>Solitamente non dovreste rinunciare alla potenza espressiva delle <code>{{...}}</code> , però c'è un caso speciale che magari <code>ng-bind</code> può essere d'aiuto: al caricamento della prima pagina della nostra webapp.</p>
<p>Quando apriamo la nostra bellissima webapp per la prima volta, il browser inizia a caricare subito il DOM, e poi inizia a scaricare e a eseguire i file javascript. Questo significa che, finchè non verrà scaricato il js di Angular, per pochi istanti vedremo le antipaticissime parentesi graffe.</p>
<p>La soluzione a questo problema è data proprio da <a href="https://docs.angularjs.org/api/ng/directive/ngBind">ng-bind</a>, ossia finchè Angular non viene caricato il browser non mostrerà alcuna parentesi graffa, e poi vedremo il testo. (Il browser, prima di scaricare Angular, non ha idea di come interpretare un attributo ng-bind e nel dubbio lo ignora).</p>
<h3>ng-bind-html, un nuovo amico</h3>
<p>Ma torniamo alla domanda iniziale. Come facciamo a mostrare l'html all'interno di una variabile <em>angularizzata</em>? La direttiva da chiamare stavolta è <a href="https://docs.angularjs.org/api/ng/directive/ngBindHtml"><strong>ng-bind-html</strong></a>, che sfortunatamente non ha corrispettivi con un'altra sintassi (niente doppie o triple parentesi qui). Il motivo per cui è meno nota (e dovrebbe essere usata con cura!) è che non bisognerebbe mai lasciare la responsabilità della formattazione del testo a un js, quindi non bisognerebbe inserire un <code><br></code> o qualsiasi altro tag in un messaggio. Inoltre sorgono dei problemi di sicurezza: che succede se un utente malevolo carica un tag <code><script></code> ?</p>
<p>Almeno per questo Angular ci mette in guardia, e infatti specifica chiaramente nella guida che per usare ng-bind-html bisogna iniettare <strong>$sanitize</strong> di angular, così da eliminare eventuali tag scomodi. Senza l'import di $sanitize non dovrebbe proprio funzionare, questo per farvi capire quanto è importante ripulire codice che potrebbe essere compromesso.</p>
<p>Morale: usate <code>ng-bind-html</code> con coscienza, ma utilizzatelo solo in pochi punti ben documentati dell'applicazione, altrimenti in futuro potreste essere anche vittima di attacchi. Nel mio caso, l'ho usato per una fix rapida su un messaggio di errore che arrivava dal server (ove risiedeva il famigerato <code><br></code> ). Quando ci sono rilasci non si può ragionare troppo 🙂</p>
Selenium WebDriver for dummies
2015-05-26T08:58:58Z
https://michelenasti.com/2015/05/selenium-webdriver-for-dummies/
<p>Esistono tool che permettono di scrivere i casi di test che simulano il comportamento dell'utente e di eseguirli all'infinito, cosa utile per individuare eventuali regressioni durante lo sviluppo.</p>
<p>Il più diffuso a livello globale, per i test di tipo funzionale, è <strong>Selenium</strong>, nelle sue varianti <strong>Selenium IDE</strong> e <strong>Selenium Web Driver</strong>. Abbiamo testato entrambi e alla fine abbiamo scelto WebDriver. (Ora esistono altri tool come Protractor, ma bisogna riconoscere che Selenium per anni è stato il più diffuso).</p>
<h3>Selenium IDE</h3>
<p>Sulla carta era la scelta migliore per gli utenti-tester: permette di memorizzare i passaggi fatti dall'utente sulla pagina, memorizzando i click del mouse e le digitazioni sulla tastiera, senza scrivere codice.</p>
<p>I problemi riscontrati sono stati i seguenti:</p>
<ul>
<li><strong>alta instabilità</strong> del sistema con il nostro applicativo, dovuto all'elevato numero di componenti caricati e agli iframe ; ciò causava crash e stalli del browser;</li>
<li>il sistema "punta e clicca" spesso <strong>non riconosce le azioni dell'utente</strong></li>
<li>per riuscire a far funzionare casi di test anche molto semplici era necessario <strong>intervenire manualmente sul codice prodotto</strong>;</li>
<li><strong>scarso supporto alle webapp moderne</strong> che usano tecnologie come AJAX, per caricare pezzi di pagine dinamicamente.</li>
</ul>
<h3>Selenium WebDriver</h3>
<p><strong>Selenium WebDriver è il motore di Selenium IDE, tuttavia è decisamente più stabile e funzionale</strong>. E' una libreria Java che, tramite la scrittura di semplici programmi, permette di automatizzare la navigazione (e il test) di pagine web. Lo svantaggio di questo approccio è che <strong>figure non tecniche, che non hanno basi di programmazione, potrebbero essere titubanti all'idea di scrivere test in un linguaggio di programmazione</strong> che non conoscono.</p>
<p>Per semplificare l'approccio alla scrittura dei casi di test ho pensato di creare una classe Java che semplifica le operazioni più comuni dei tester. Alcune funzionalità messe a disposizione sono:</p>
<ul>
<li><strong>apertura e chiusura del browser</strong></li>
<li><strong>navigazione</strong> verso un indirizzo specifico</li>
<li><strong>login</strong> e <strong>logout</strong></li>
<li>possibilità di <strong>recuperare un oggetto dalla pagina</strong> (per poterlo poi utilizzare, ad esempio per simulare un click, o inviare testo, etc)</li>
<li>possibilità di <strong>navigare all'interno dei frame</strong>, cosa che con Selenium IDE è risultata particolarmente difficile;</li>
<li>possibilità di <strong>eseguire i test in batteria</strong>, su un server remoto con accesso a un browser, e di ottenere un report dei casi di test che falliscono</li>
</ul>
<p>Una delle funzioni più complesse che abbiamo dovuto realizzare, nell'ottica delle webapp asincrone, è di <strong>aspettare che un componente venisse caricato e mostrato all'interno della pagina</strong>. Il nostro approccio permette di aspettare (per un tempo prefissato) che un componente sia caricato e mostrato all'utente, e solo dopo renderlo disponibile al test. <em>Se non si seguisse questo approccio, si correrebbe il rischio di usare componenti non ancora caricati, o cliccare su elementi nascosti.</em></p>
<p>Tutto ciò che è stato descritto in questo semplice documento è in teoria possibile realizzarlo anche con Selenium IDE; a causa dei frequenti crash e stalli del sistema abbiamo notato come l'approccio WebDriver sia risultato più semplice sia per il tester sia per colui che dovrà eventualmente continuare l'evoluzione della libreria.</p>
<h3>Il codice</h3>
<p>riporto la classe che abbiamo scritto per i nostri test. Questa classe va ereditata dai vostri test junit e i suoi metodi pubblici sono ciò che potrebbero servirvi di più.</p>
<p>Il metodo più utile, per noi, è <code>waitAndReturnTheObject</code> che aspetta che l'elemento venga creato nel DOM e mostrato all'utente, e una volta recuperato lo restituisce. Gli altri metodi hanno un javadoc che potreste trovare utile.</p>
<p>Essendo un JUnit non dovreste avere troppi problemi a eseguire i problemi con un tool come Maven.</p>
<pre class="language-java"><code class="language-java"><span class="token keyword">package</span> <span class="token namespace">utils</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token import static"><span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token static">fail</span></span><span class="token punctuation">;</span><br /><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">List</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">Callable</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">ExecutionException</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">ExecutorService</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">Executors</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">Future</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">TimeUnit</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span></span><span class="token class-name">TimeoutException</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span>logging<span class="token punctuation">.</span></span><span class="token class-name">Logger</span></span><span class="token punctuation">;</span><br /><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">After</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Before</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>openqa<span class="token punctuation">.</span>selenium<span class="token punctuation">.</span></span><span class="token class-name">By</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>openqa<span class="token punctuation">.</span>selenium<span class="token punctuation">.</span></span><span class="token class-name">NoSuchElementException</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>openqa<span class="token punctuation">.</span>selenium<span class="token punctuation">.</span></span><span class="token class-name">WebDriver</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>openqa<span class="token punctuation">.</span>selenium<span class="token punctuation">.</span></span><span class="token class-name">WebElement</span></span><span class="token punctuation">;</span><br /><span class="token keyword">import</span> <span class="token import"><span class="token namespace">org<span class="token punctuation">.</span>openqa<span class="token punctuation">.</span>selenium<span class="token punctuation">.</span>firefox<span class="token punctuation">.</span></span><span class="token class-name">FirefoxDriver</span></span><span class="token punctuation">;</span><br /><br /><span class="token comment">/**<br /> * Classe statica che aiuta a sviluppare test con Selenium Web driver.<br /> */</span><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SeleniumWebDriverBaseTest</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">protected</span> <span class="token class-name">WebDriver</span> driver<span class="token punctuation">;</span><br /> <span class="token keyword">protected</span> <span class="token class-name">String</span> baseUrl<span class="token punctuation">;</span><br /> <span class="token keyword">private</span> <span class="token class-name">StringBuffer</span> verificationErrors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuffer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ExecutorService</span> service <span class="token operator">=</span> <span class="token class-name">Executors</span><span class="token punctuation">.</span><span class="token function">newSingleThreadExecutor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token constant">TIMEOUT</span> <span class="token operator">=</span> <span class="token number">30</span><span class="token punctuation">;</span><br /> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Logger</span> <span class="token constant">LOGGER</span> <span class="token operator">=</span> <span class="token class-name">Logger</span><span class="token punctuation">.</span><span class="token function">getLogger</span><span class="token punctuation">(</span><br /> <span class="token class-name">SeleniumWebDriverBaseTest</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getSimpleName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><br /> <span class="token annotation punctuation">@Before</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setUp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span><br /> driver <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FirefoxDriver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> baseUrl <span class="token operator">=</span> <span class="token string">"http://localhost:8080/"</span><span class="token punctuation">;</span><br /> driver<span class="token punctuation">.</span><span class="token function">manage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">timeouts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">implicitlyWait</span><span class="token punctuation">(</span><span class="token constant">TIMEOUT</span><span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span><span class="token constant">SECONDS</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> driver<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>baseUrl<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"connecting to "</span> <span class="token operator">+</span> baseUrl<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token annotation punctuation">@After</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">tearDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span><br /> driver<span class="token punctuation">.</span><span class="token function">quit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token class-name">String</span> verificationErrorString <span class="token operator">=</span> verificationErrors<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token string">""</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>verificationErrorString<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">fail</span><span class="token punctuation">(</span>verificationErrorString<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * Checks if the element is present into the DOM.<br /> * Caution: it will answer true even if the element is present, but not visible. @see<br /> * waitForLoadedAndDisplayed<br /> *<br /> * @param by the selector to reach the element<br /> * @return true if the element is found<br /> */</span><br /> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isElementPresent</span><span class="token punctuation">(</span><span class="token class-name">By</span> by<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">try</span> <span class="token punctuation">{</span><br /> driver<span class="token punctuation">.</span><span class="token function">findElement</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchElementException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * checks that the element is shown on video<br /> *<br /> * @param by the selector<br /> * @return true if the element is shown on video<br /> */</span><br /> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isElementDisplayed</span><span class="token punctuation">(</span><span class="token class-name">By</span> by<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">try</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> driver<span class="token punctuation">.</span><span class="token function">findElement</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isDisplayed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchElementException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * Stops the test for some time; useful to check if everything is going good.<br /> *<br /> * @param milliseconds<br /> */</span><br /> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">pause</span><span class="token punctuation">(</span><span class="token keyword">long</span> milliseconds<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">try</span> <span class="token punctuation">{</span><br /> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span>milliseconds<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * This method checks that the element has been loaded into the DOM and shown.<br /> *<br /> * @param by the element selector<br /> */</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">waitForLoadedAndDisplayed</span><span class="token punctuation">(</span><span class="token keyword">final</span> <span class="token class-name">By</span> by<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"waiting for "</span> <span class="token operator">+</span> by<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">final</span> <span class="token class-name">Future</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Void</span><span class="token punctuation">></span></span> future <span class="token operator">=</span> service<span class="token punctuation">.</span><span class="token function">submit</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Callable</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Void</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">public</span> <span class="token class-name">Void</span> <span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span><br /> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isElementPresent</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">isElementDisplayed</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>by <span class="token operator">+</span> <span class="token string">" found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">try</span> <span class="token punctuation">{</span><br /> future<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token constant">TIMEOUT</span><span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span><span class="token constant">SECONDS</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">InterruptedException</span> <span class="token operator">|</span> <span class="token class-name">ExecutionException</span> <span class="token operator">|</span> <span class="token class-name">TimeoutException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">fail</span><span class="token punctuation">(</span><span class="token string">"search for "</span> <span class="token operator">+</span> by <span class="token operator">+</span> <span class="token string">" went timeout!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * utility method to move to other frames. <br /> * beware: it will always return to the parent frame first.<br /> *<br /> * @param url url to check if the frame contains it.<br /> */</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">switchToFrame</span><span class="token punctuation">(</span><span class="token class-name">String</span> url<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> driver<span class="token punctuation">.</span><span class="token function">switchTo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">defaultContent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"Switchig to frame "</span> <span class="token operator">+</span> url<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">WebElement</span><span class="token punctuation">></span></span> framesList <span class="token operator">=</span> driver<span class="token punctuation">.</span><span class="token function">findElements</span><span class="token punctuation">(</span><span class="token class-name">By</span><span class="token punctuation">.</span><span class="token function">xpath</span><span class="token punctuation">(</span><span class="token string">"//iframe"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token class-name">WebElement</span> selectedFrame <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">WebElement</span> frame <span class="token operator">:</span> framesList<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>frame<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"src"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> selectedFrame <span class="token operator">=</span> frame<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"Selected Frame: "</span> <span class="token operator">+</span> selectedFrame<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">"src"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> driver<span class="token punctuation">.</span><span class="token function">switchTo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">frame</span><span class="token punctuation">(</span>selectedFrame<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"switched!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * go back to the default frame (the main document)<br /> *<br /> */</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">switchToDefaultFrame</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> driver<span class="token punctuation">.</span><span class="token function">switchTo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">defaultContent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/**<br /> * calls "waitForLoadedAndDisplayed(by)" and returns the element found, <br /> * that can be used easily.<br /> * @param by element the selector.<br /> * @return the WebElement chosen<br /> */</span><br /> <span class="token keyword">public</span> <span class="token class-name">WebElement</span> <span class="token function">waitAndReturnTheObject</span><span class="token punctuation">(</span><span class="token class-name">By</span> by<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">waitForLoadedAndDisplayed</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token class-name">WebElement</span> element <span class="token operator">=</span> driver<span class="token punctuation">.</span><span class="token function">findElement</span><span class="token punctuation">(</span>by<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token constant">LOGGER</span><span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>element <span class="token operator">+</span> <span class="token string">" found!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> element<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<h3>Dipendenze Maven</h3>
<p>Queste sono le dipendenze necessarie:</p>
<pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.seleniumhq.selenium<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>selenium-java<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>2.45.0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>junit<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>junit<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>4.12<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>pre</span><span class="token punctuation">></span></span></code></pre>
<h3>Cosa manca?</h3>
<ul>
<li>sostituire la classe di logging con una più seria</li>
<li>caricare un progetto di esempio e un esempio di utilizzo</li>
<li>scrivere questa pagina in inglese</li>
</ul>
iFrame, Safari, iOs e la lotta all'ultimo millisecondo
2015-05-27T08:00:27Z
https://michelenasti.com/2015/05/iframe-safari-ios-e-la-lotta-allultimo-millisecondo/
<p>Tutto è nato da una richiesta innocua da parte del capo: la webapp che abbiamo realizzato, e che deve operare principalmente sui pc del nostro cliente, sarà mostrata ad un'importante fiera a fine maggio, quindi dobbiamo rivederla e ottimizzarla per tablet.</p>
<p>Poco male, abbiamo usato Bootstrap per la UI, quindi si tratterà solo di limare, aggiustare, riorganizzare gli elementi sullo schermo.</p>
<p>E invece ciò che stiamo affrontato è divenuto un bagno di sangue.</p>
<h3>Bootstrap, questo sconosciuto</h3>
<p>Quando si lavora in un team di 15 persone e tutti fanno tutto ci si scontra con un'amara verità: non siamo tutti egualmente predisposti per il "bello", e soprattutto non siamo tutti disposti a capire come funziona bootstrap. Praticamente: <strong>nessuno si legge la documentazione e spesso vedo usare cose complicate quando ci sono alternative più facili e più portabili</strong>. Un esempio: alcuni amici hanno eliminato il padding a tutti gli elementi aggiungendo classi come ResetPadding, e poi si lamentavano che i componenti erano disposti male sullo schermo. :-O</p>
<h3>iFrame e iOs, nemici giurati</h3>
<p>Tutto il mondo usa gli iFrame per una sola cosa, ossia per mostrare la pubblicità. Gli iframe sono elementi che caricano al loro interno altre pagine, le quali pagine riscaricano da capo html, css, javascript, immagini, etc. Ossia, se usate lo stesso script sia fuori che dentro l'iFrame, questo verrà scaricato due volte.</p>
<p>Il mondo del web quindi cerca di non usare gli iFrame per qualcosa che non sia pubblicità. Noi invece cosa abbiamo fatto? <strong>Approfittando di un framework "fatto in casa", basato su Angular, caricavamo le pagine all'interno di un iFrame</strong>. Ossia: la toolbar in alto e il menu laterale fanno parte della pagina, il contenuto vero e proprio invece viene caricato ogni volta all'interno di un iframe.</p>
<p><img src="https://i1.wp.com/stevesouders.com/efws/images/1301-iframes-cost-of-elements.gif" alt="tempo per aggiungere al DOM 100 elementi" /></p>
<p>L'impatto di questo approccio è stato devastante: se pure lo sviluppo era in qualche modo semplificato, ogni volta che cliccavamo su una pagina veniva ricaricato tutto, da Angular fino al css, e non so se lo sapete ma finchè angular non viene caricato l'utente vede le parentesi graffe (<code>{{ }}</code> ) tipiche di Angular. Inoltre sui tablet avevamo un degrado delle prestazioni, perchè gli iFrame sono gli elementi più pesanti da aggiungere al DOM e tutto il traffico di rete che generavamo a ogni pagina ci rubava secondi preziosi. Su un tablet più datato avevamo una pagina che si caricava in 40 secondi ! Un esempio di benchmark lo trovate qui a lato.</p>
<p>Altri due gravissimi problemi: <strong>su iPad alcuni iframe (non tutti) non scrollano</strong>; inoltre avevamo trovato un bug che <strong>dopo un po' di navigazione della nostra webapp, safari crasha senza dare troppe spiegazioni</strong>.</p>
<h3>Alcune soluzioni messe in campo</h3>
<p>Tutto questo ci ha fatto andare un po' in panico e stiamo cercando le dovute contromisure, che chiaramente non possiamo attuare tutte visto che la scadenza è tra pochi giorni. Però qualcosa abbiamo fatto ed è giusto condividerle con voi, magari qualcuno passa di qui e ci da qualche dritta fantastica.</p>
<ul>
<li>sembrerà una cretinata, ma <strong>mettere tutti gli script alla fine (giusto un attimo prima del tag di chiusura </body>) diminuisce considerevolmente il tempo di caricamento delle pagine</strong>. E' una di quelle best practices che non si considera mai e che invece aiuta davvero.</li>
<li><strong>Diminuire all'osso il numero di file scaricati</strong>, quindi minimizzare e comprimere tutti i javascript, css, e html. Farlo con java e maven è un po' complicato, ma si può fare. Poi scriverò un articolo su come precaricare tutti i template delle direttive di angular.</li>
<li>Sul caricamento degli iFrame: <strong>uno degli approcci più utilizzati è quello di aggiungere l'iFrame al dom, aspettare 10 millisecondi, e poi settare l'attributo src, così da ritardare leggermente il caricamento del frame</strong>. Alcuni benchmark mostrano che questo approccio è efficace, anche se la miglior soluzione è di evitare l'iFrame. Trovate una discussione su questo argomento a <a href="http://www.aaronpeters.nl/blog/iframe-loading-techniques-performance#normal">questo link</a>.</li>
<li>l'iPad non scrolla e sistematicamente crasha? <strong>useremo tablet Android</strong>.</li>
<li>Eliminare chiamate asincrone che non servono davvero... Il miglior approccio è di fare una chiamata sola per precaricare tutte le combo che serviranno nella pagina.</li>
</ul>
<p>La soluzione a lungo termine è di modificare questo nostro framework e di usare qualcosa di più standard, come ui-router o ng-router di angular. Ciò che vorremmo evitare è di riscrivere tutte le pagine che abbiamo già realizzato, quindi cercheremo di usare l'approccio meno invasivo possibile.</p>
iMovie and the -50 error
2015-06-22T19:00:16Z
https://michelenasti.com/2015/06/imovie-50-error/
<p>Yesterday I had to make a quick video for a family celebration so I decided to use <strong>iMovie</strong> for the first time since I had bought my Mac in 2012.</p>
<p>iMovie is smart and it is very easy to prepare a video without being an expert of video editing. Of course it was what I was searching for, and in about 2 hours I managed to make the video.</p>
<p>The problem arose when I tried to create a <strong>mpg file</strong> of the video. There's a button <strong>"share" -> "file"</strong> with some settings about the video, and I simply left all with default values.</p>
<p>My first 5-6 attempts where blocked by a very strange <strong>-50 error</strong>. There was <strong>no description of what was going on</strong>, so I tried to be friend with Google. Unfortunately they all described error -49 and I was a bit disappointed, however it seems that nobody really knows where's the problem and here are the suggestions that worked for me:</p>
<ul>
<li>disable Time Machine while rendering the video;</li>
<li>disable iCloud;</li>
<li>disable other cloud services that are scanning the rendering directory (dropbox, google drive, owncloud, etc. In my case, owncloud was scanning Pictures folder).</li>
</ul>
<p>At first, none of these suggestions solved the problem.</p>
<p>Another strange thing was that <strong>the video was stopping always at the same point</strong> - I could see the output video, and instead of 4 minutes it was about 3:15 minutes. I reviewed the video and I found something corrupted, like a flickering in the video; it worked fine during preview but the Renderer Job failed for this. I deleted this video and tried again. (In reality, I only dropped the corrupted frames, removing the bad part from the clip).</p>
<p><strong>The video still gave -50 errors , but to my surprise, the rendering process was continuing.</strong> At the end the video was completed and it played well in Vlc and QuickTime. So My suggestions are to</p>
<ol>
<li>disable the services listed before</li>
<li>check if the rendering blocks always at the same point</li>
<li>check if the rendering really stops or if continues until the end. Ignore errors that don't block the rendering.</li>
</ol>
<p>While waiting for an Apple iFix, maybe this will save 3-4 hours to understand what's going on.</p>
Impostare PHPUnit in IntelliJ
2015-06-24T13:30:12Z
https://michelenasti.com/2015/06/impostare-phpunit-in-intellij/
<p>Se state lavorando a un progetto creato col framework <strong>Symfony2,</strong> in <strong>PHP</strong>, è molto probabile che state cercando un IDE che vi permette di utilizzare tutti gli strumenti necessari allo sviluppo.</p>
<p><strong>Symfony2</strong> utilizza una serie di tecnologie consolidate in PHP tra cui <strong>Composer</strong>, che è un gestore delle dipendenze come Maven per Java; per i test unitari c'è <strong>PHPUnit</strong>, come ORM c'è <strong>Doctrine</strong> al posto di Hibernate, e così via.</p>
<p>Non è un mistero che faccio uso di IntelliJ da qualche meso a questa parte. Vediamo come configurare PHPUnit !</p>
<p>Appena creato il progetto con Symfony, impostare Composer dalla relative impostazioni:</p>
<p>|<a href="https://michelenasti.com/uploads/2015/06/Schermata-2015-06-22-alle-23.09.201.png"></a></p>
<p>Troverete 2 file, uno .phar e uno .json, inseriteli nei relativi textbox.</p>
<p>Successivamente bisogna impostare PHPUnit, che <a href="https://www.jetbrains.com/idea/help/enabling-phpunit-support.html#d683218e298">secondo questa guida</a> dobbiamo seguire quella al passo 2 (trovate il file autoload.php):</p>
<p><img src="https://michelenasti.com/uploads/2015/06/Schermata-2015-06-22-alle-23.23.02.png" alt="" /></p>
<p>Non ci resta che lanciare i test impostando il comando dal menù dei task:</p>
<p><img src="https://michelenasti.com/uploads/2015/06/Schermata-2015-06-22-alle-23.29.02.png" alt="" /></p>
<p>Se provate a lanciare questo task, vedrete i test eseguiti all'interno delll'IDE !</p>
Alcune Best Practices per non impazzire con i DB relazionali
2015-07-03T18:30:16Z
https://michelenasti.com/2015/07/alcune-best-practices-per-non-impazzire-con-i-db-relazionali/
<p>Cerchiamo di avere poche ma semplici regole di gestione del DB che servono a non impazzire in futuro, fin'ora quelle più semplici che ho usato e che hanno portato alla miglior comprensione possibile (parlo di un team di 30 devs) è:</p>
<ul>
<li><strong>Chiamare tutte le tabelle al singolare</strong> (es. PRODOTTO e non PRODOTTI)</li>
<li><strong>Chiamare tutte le tabelle in inglese</strong> (quindi: Product)</li>
<li><strong>Tutte le tabelle devono avere un solo ID</strong> (niente chiavi composite!) - di tipo String o Long.
<ul>
<li><strong>String</strong> quando nel db ci saranno parecchi inserimenti e potremmo usare una funzione per generare stringhe casuali (UUID) da usare come ID.</li>
<li><strong>Long</strong> quando ci sono pochi inserimenti ma molte letture, così da facilitare gli indexer. Supponiamo di avere 30000 inserimenti simultanei, il 30.000esimo deve aspettare che i precedenti 29.999 record abbiano ricevuto un ID numerico. In questo caso conviene usare la strategia precedente (String).</li>
</ul>
</li>
<li>quando ci sono relazioni con altre tabelle, la colonna che gestisce la relazione si chiamerà <code>NOMEALTRATABELLA_ID</code>
<ul>
<li>Quando ci sono tabelle che mappano <strong>relazioni molti-a-molti</strong>, chiamare la tabella semplicemente <code>ENTITY1_ENTITY2</code> , magari provando a inserire come nome della prima entità quella che "gestisce" la relazione. Però non è davvero importante l'ordine in cui compaiono, l'importante è che si segua una convenzione.</li>
</ul>
</li>
<li>evitare di chiamare colonne con il prefisso <code>NOT_</code> e similari, visto che il nostro cervello fa uno sforzo algebrico per capirne il significato (es. <code>NOT_PRESENT</code> a false, vuol dire che è presente... Ma perchè non chiamare la colonna PRESENT e settare il valore a true?!)</li>
</ul>
<p>ho lavorato in un'azienda dove il DB di 3000 tabelle era organizzato in questo modo e si capiva tutto. Hibernate quasi non necessitava di configurazione. Ora lavoro in un posto dove il db è vecchio 30 anni e hano fatto il contrario di queste regole; non si capisce una vacca.</p>
Instantiate beans with Spring without @Autowired
2015-07-14T18:30:45Z
https://michelenasti.com/2015/07/instantiate-beans-with-spring-without-autowired/
<p>Are you in a situation where @Autowired will not work? for example, in a static class or a class that is istantiated by another framework (hibernate) and so on. The latter case is my case: I had to autowire a bean in a Sequence Generator (to create ID for my classes), but since the class was instantiated by Hibernate, Spring could not access and autowire it, even if it was declared as a bean.</p>
<p>However, there is a chance to get what you want. In my example we will try to autowire "myBean" object. Please ensure that your bean is declared somewhere in your applicationContext.xml 😉</p>
<ol>
<li>First of all, the class that can't be Spring-ified must implement ApplicationContextAware interface :</li>
</ol>
<p><pre class="lang:java mark:2,4 decode:true">public class SequenceTableIdGenerator implements IdentifierGenerator,
ApplicationContextAware {</p>
<p>private static ApplicationContext applicationContext;</p>
<p>private MyBean myBean;</p>
<p>...</p>
<p>}</pre></p>
<ol start="2">
<li>you must override the method setApplicationContext that comes with the above interface :</li>
</ol>
<p><pre class="lang:java decode:true">@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}</pre></p>
<ol start="3">
<li>Finally, you can autowire your beans in a constructor or in any other method:</li>
</ol>
<p><pre class="lang:java mark:2 decode:true">if (myBean == null) {
myBean = (MyBean) applicationContext.getBean("myBean");
}</pre></p>
<p>That's it! This method is not standard but it's very useful. Every time I needed it I had to go searching the web. Now this useful piece of info is my website 🙂</p>
Ci ho messo del tempo per capire il passaggio di parametri delle funzioni Javascript
2015-07-21T18:30:10Z
https://michelenasti.com/2015/07/ci-ho-messo-del-tempo-per-capire-il-passaggio-dei-parametri-in-javascript/
<p>Oggi vi parlo di qualcosa che sin dall'inizio non avevo compreso a fondo: gli argumenti delle funzioni in Javascript (o ECMAScript, che sarebbe il nome completo). Quando un anno fa ho compreso questa storia degli "<em>arguments</em>" mi si è aperto un mondo!</p>
<p>Anni fa infatti mi chiedevo com'è possibile che in JS si possa passare un numero arbitrario di parametri alle funzioni, per di più senza specificare il tipo degli argomenti. All'epoca (circa 10 anni fa) Java era il mio linguaggio preferito, e avrei voltuo che tutto somigliasse a Java. Per fortuna così non è!</p>
<p>A una funzione JS non importa quanti parametri vengono passati, nè si interessa del tipo. Ad esempio, potreste definire una funzione con due parametri ma poi potreste passarne tre, o uno, zero, mille - davvero, non è <em>sintatticamente</em> importante.</p>
<p>Questo accade perchè <strong>gli argomenti di una funzione JS vengono rappresentati internamente come un array</strong>. L'array si chiama <code>arguments</code> e lo si può utilizzare all'interno di una funzione per ricavare il valore degli elementi passati.</p>
<p>Si può accedere a tutti i parametri di una funzione usando la notazione con le parentesi quadre, dunque, e <code>arguments[0]</code> sarà il primo parametro, <code>arguments[1]</code> il secondo, e così via. Si può utilizzare <code>arguments.length</code> per conoscere il numero di parametri passati.</p>
<p>Dunque potremmo scrivere una funzione nel modo classico di JS (esplicitando i parametri):</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"ciao "</span> <span class="token operator">+</span> name <span class="token operator">+</span> <span class="token string">", "</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>oppure usando <code>arguments</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"ciao "</span> <span class="token operator">+</span> arguments<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">", "</span> <span class="token operator">+</span> arguments<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>Come si può vedere, la funzione sayHi è stata definita senza nessun argomento in input, eppure si comporterà allo stesso modo. Questo mostra anche una delle caratteristiche di Javascript: <strong>i parametri col nome sono una convenienza, non una necessità</strong>.</p>
<p>Ne approfitto anche per dire che, se usate lo <em>"strict mode"</em> di JS, non potrete assegnare valori a <code>arguments</code>. Ossia <code>arguments[0] = "ciao"</code> restituirà un errore!</p>
<p>Gli argomenti col nome possono anche essere utilizzati con arguments, javascript non si lamenterà.</p>
<h3>L'overloading in Javascript (che non esiste)</h3>
<p>L'overloading, così come definito negli altri linguaggi, in JS semplicemente non esiste. Per ottenere un effetto simile, ossia una funzione che si comporta diversamente in base al numero o al tipo degli argomenti in input, dobbiamo fare una sorta di switch su <code>arguments.length</code> o sul tipo degli argomenti. Ciò che realmente conta in JS è il nome della funzione, e se due funzioni hanno lo stesso nome, vale sempre l'ultima analizzata.</p>
<p>Analizziamo il seguente snippet di codice:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">aggiungiUnNumero</span><span class="token punctuation">(</span><span class="token parameter">num</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> num<span class="token operator">+</span><span class="token number">100</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">function</span> <span class="token function">aggiungiUnNumero</span><span class="token punctuation">(</span><span class="token parameter">num</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> num1<span class="token operator">+</span><span class="token number">200</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">aggiungiUnNumero</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token comment">//300</span></code></pre>
<p>la seconda definizione di aggiungiUnNumero ha sovrascritto la prima !</p>
<h3>Conclusioni</h3>
<p>Ci sono tante parti di JS che ho capito tardi, altre che non ho ancora capito. Una cosa che mi ha fatto intuire la potenza di JS è il fatto che una funzione può prendere in input altre funzioni, e restituirne una come output. Di questo parlerò in futuro. Adios!</p>
Informatica Antincendio
2015-07-26T19:25:19Z
https://michelenasti.com/2015/07/informatica-antincendio/
<p>Si: l'articolo che sto per scrivere parla proprio di questo: <strong>Informatica Antincendio</strong>.</p>
<p>E' che dietro casa mia ci sono almeno 2 incendi all'anno, e la collina che sovrasta casa mia è praticamente brulla e senza alberi.</p>
<p>Così ho pensato, forse è possibile tirare fuori un <em>modello predittivo per gli incendi</em> che, basandosi sul passato, riesca a dirmi quando ci saranno i prossimi roghi.</p>
<p>Sembra fantascienza, però ci ho pensato e non è un'idea tanto campata in aria. In fondo riusciamo a prevedere che tempo farà domani, forse possiamo prevedere anche gli incendi!</p>
<p>Ecco tutto quello che so a riguardo, avendone spenti almeno due all'anno negli ultimi dieci.</p>
<ul>
<li>sono di matrice umana, c'è un piromane e tutti stiamo aspettando che muoia.</li>
<li>Il piromane guarda il meteo e sceglie sempre il giorno migliore per farlo.</li>
<li>Quando si sviluppa un incendio, in genere il meteo è
<ul>
<li>ventoso</li>
<li>caldo</li>
<li>non dovrà piovere il giorno sucessivo</li>
</ul>
</li>
</ul>
<p>Potremmo creare un database con gli ultimi incendi e memorizzare queste info:</p>
<ul>
<li>data e ora di quando è partito l'incendio</li>
<li>temperatura</li>
<li>direzione e forza del vento</li>
<li>meteo del giorno successivo.</li>
</ul>
<p>L'idea è che se il giorno successivo piove non ha senso appiccare un incendio. Le informazioni sulla direzione del vento sono utili per predire dove sarà appiccato il fuoco: se il vento soffia a Nord conviene appiccare l'incendio a Sud.</p>
<p>A questo punto, dovrebbe essere piuttosto semplice tirare fuori una correlazione statistica del passato. La mia idea è che tre cose le conosceremo in anticipo, ossia la temperatura e il vento di un dato giorno e il meteo del giorno successivo; questo ci permetterebbe di localizzare da dove partirà l'incendio e ci potrebbe permettere di pattugliare le aree sensibili.</p>
<p>Purtroppo mi rendo conto che serve l'aiuto delle istituzioni per conoscere i dati degli ultimi incendi (ma potrei usare la montagna dietro casa mia come banco di prova). E poi ci sarebbe da lavorare un po' sulla probabilità degli eventi ... Insomma potrebbe essere un ennesimo strumento utile contro gli incendi.</p>
<p>Voi che ne pensate?</p>
Css Position
2015-07-27T19:00:55Z
https://michelenasti.com/2015/07/css-position/
<p>Facciamo una breve deviazione sul CSS.</p>
<p>l’attributo **position **viene utilizzato per specificare la posizione degli elementi all’interno della pagina html. Sono 4 i suoi possibili valori:</p>
<ul>
<li>**static **– è il valore che viene assegnato dal browser quando non viene specificato nulla. L’elemento viene aggiunto e posizionato in base all regole standard dell’html.</li>
<li><strong>fixed</strong> – l’elemento viene posizionato in base alla finestra del browser, quindi è anche insensibile allo scrolling. Utile per i menù che devono essere sempre visibili.</li>
<li><strong>relative</strong> – posiziona l’elemento _relativamente _alla posizione che avrebbe avuto se fosse stato static. Per essere più chiari: posiziona l’elemento in base a dove sarebbe dovuto essere. Molto spesso viene associato a un elemento che poi dovrà contenere</li>
<li><strong>absolute</strong> – posiziona l’elemento relativamente all’oggetto contenitore più prossimo che ha la position diversa da <em>static</em>. Se non viene trovato nulla, si risale fino a <em>html</em>.</li>
</ul>
<p>Una volta impostate queste proprietà, se applicabili, si possono utilizzare i vari **top, left, bottom, right. **</p>
<p>Questa tabellina andrebbe imparata a memoria da chiunque debba mettere mano, almeno una volta, al codice html & css di una pagina web!</p>
loggare il tempo di esecuzione di un JS : console.time e console.timeEnd
2015-07-28T20:30:09Z
https://michelenasti.com/2015/07/loggare-il-tempo-di-esecuzione-di-un-js-console-time-e-console-timeend/
<p>Ogni browser moderno dispone di avanzatissimi strumenti di sviluppo, Firefox è stato il primo con Firebug (ora integra un suo strumento interno) mentre Chrome ha dettato lo standard con Chrome Developer Tools. A seguire tutti i browser che volevano definirsi moderni hanno dovuto creare delle conosole di sviluppo "serie", e supportare tutta una serie di costrutti Javascript per facilitare lo sviluppo e il debugging di queste applicazioni.</p>
<p>Se leggete questo blog significa che sapete aprire una di queste console e probabilmente lo fate ogni giorno per lavoro. Ma come misurare che il codice JS che stiamo scrivendo sia effettivamente veloce? O meglio, come valutarlo in relazione ad altre soluzioni alternative?</p>
<p>un tool estremamente utile è <code>console.time()</code> . Tutto il codice racchiuso tra <code>console.time()</code> e <code>console.timeEnd()</code> viene misurato in console. Per identificare il blocco di codice valutato possiamo (e secondo me dobbiamo) dare una stringa come label. Vediamo un esempio:</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">time</span><span class="token punctuation">(</span><span class="token string">"test del for"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br /><span class="token keyword">var</span> i<span class="token punctuation">,</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <br /><span class="token keyword">for</span><span class="token punctuation">(</span>i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">&</span>lt<span class="token punctuation">;</span><span class="token number">100000</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <br /> arr<span class="token punctuation">.</span><span class="token function">unshift</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span> <br />arr<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br />console<span class="token punctuation">.</span><span class="token function">timeEnd</span><span class="token punctuation">(</span><span class="token string">"test del for"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Nel caso precedente creiamo un array di 100.000 posizioni, e inseriamo ogni nuovo elemento all'inizio. Dunque gli elementi sono disposti in odine decrescente. Poi lo ordiniamo con sort(). In console leggiamo: <code>test del for: 1041.271ms</code> .</p>
<p>Vediamo una variante che ci aspettiamo più semplice:</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">time</span><span class="token punctuation">(</span><span class="token string">"test del for"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br /><span class="token keyword">var</span> i<span class="token punctuation">,</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <br /><span class="token keyword">for</span><span class="token punctuation">(</span>i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">&</span>lt<span class="token punctuation">;</span><span class="token number">100000</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <br /> arr<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token operator">=</span>i <br /><span class="token punctuation">}</span> <br />arr<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br />console<span class="token punctuation">.</span><span class="token function">timeEnd</span><span class="token punctuation">(</span><span class="token string">"test del for"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In questo altro caso invece creiamo l'array inserendo gli elementi nell'ordine naturale. Dopo l'operazione di ordinamento viene loggato <code>test del for: 187.685ms</code> .</p>
<p>Cosa abbiamo dunque scoperto? che conviene ordinare array già ordinati 🙂 e che console.time(LABEL) e console.timeEnd(LABEL) sono una comoda funzione per verificare quanto tempo viene impiegato dal nostro codice javascript.</p>
<p>Esercizio per casa: conviene usare <code>document.getElementById("body").**createElement()</code> o <code>document.getElementById("body").innerHtml()</code>? A voi la scoperta!</p>
the story of a programmer (me) that failed in PHP
2015-09-08T19:22:35Z
https://michelenasti.com/2015/09/the-story-of-a-programmer-me-that-failed-in-php/
<p>Some time ago a friend of mine called and said: "<em>Hey! I just coded the app I was thinking about three years ago</em>, <strong>would you want to join me</strong>?"</p>
<p>The incredible thing about this is that <strong>my friend is a law student</strong>. He had <strong>no idea about programming</strong>, so he learned everything by himself.</p>
<p><strong>Three years before, he asked me to code his dream</strong> - an e-commerce idea to help websites to get more clients & revenue. I had just graduated, and <strong>I had the feeling that I could not program the amazing things that I saw around me.</strong> Everybody was doing great things, and <strong>all I knew about Computer Science was a bunch of (very important) theorems</strong> about what you can and what you can't do with a computer.</p>
<p>So, <strong>I had to refuse the offer</strong> - starting a startup and having the feeling of not knowing how to work was my biggest fear..</p>
<p>Then I got a regular job as a computer programmer, starting to code in <em>Java</em> and its related technologies (Hibernate, Maven, GWT, than AngularJS).</p>
<p>After three years I have much more control and power on my capacities, so I feel like <strong>everything is feasible</strong> (<em>the opposite of three years before</em>!).</p>
<p>Returning to my friend, I was very surprised that he could code his idea, and even if the code that I saw was not perfect (it was coded in <strong>PHP</strong> but using a framework called <strong>Symfony</strong>), it was CODE that worked someway.</p>
<p>When he asked me to join him and help I had some concerns:</p>
<ol>
<li>I am a <em>Java</em> programmer. In the last 3 years of my life, plus the years of university, I have studied Java as my primary programming language. I am using it every day. <strong>In PHP I have very little experience</strong>, based no what it was 5-6 years ago (a big amount of functions with no namespaces; queries, logic & controllers where all together, everywhere. A big mess.).</li>
<li>I have <strong>little to no time to dedicate to the project</strong>. since I have my job that is 9 to 18, from monday to friday, I have only the weekend to work on such things. And I usually have to care about my family, my house and my life. So ... how to find the right amount of time?</li>
<li><strong>I wanted to work to a side projects</strong> to improve my CV and to learn new things. <strong>Unfortunately I was unattracted by PHP</strong> and its technology stack.</li>
</ol>
<p>I managed to resolve the second problem: since I have to travel 45 minutes to go to work, I would work in the bus. It's about 1,5 hrs of work everyday.</p>
<p>But I found myself having to study symfony for everything I wanted to do. the shift from Java to Php is not so great but annoying; syntax glitches like <code>$object.method()</code> and <code>$object->method()</code> (just to say the first!) have made to loose many hours in debug time. (both the expressions are legal in php so you don't get errors!)</p>
<p>I've spent many hours studying Symfony documentation without studying enough PHP (newest) syntax.</p>
<p>Finally, I managed to do some small bugfixes but still could not make any substantial modification to the codebase.</p>
<h3>So what's going on?</h3>
<p>After many months I had another personal problem so I had less time to dedicate to this project. At the end I had to inform my partner that I could not help him anymore, at least for now.</p>
<p>This was not an easy decision for me but then I understood that I was asking more from myself, more than I could handle.</p>
<p>Lessons learned:</p>
<p>First, <strong>if you want to _startup _you should code in a language that you feel comfortable</strong>. My language is Java (not because it's the best, but because I already know how to do everything!) and I have never thought to learn PHP. I have experimented in Node, Ruby, Python... but not PHP. this language was not a priority for me and day after day I ended up hating it.</p>
<p>Second, <strong>if you don't have time to do things, and you also have to learn before you do, you are going to hit your head against a hard wall.</strong> I say this because I think of myself as an average (==> good!) programmer, and I believe that I can learn everything (==> I'm a <a href="http://michelenasti.com/2015/02/quando-assumere-generalisti-vs-specialisti/">generalist</a>) <em>given the right amount of time</em>. And I don't have time. So ... It was hard to say, but I cannot work to side projects that demand high availability.</p>
<p>This was harder to accept for me than for my friend.</p>
<p>I really want that he finds a good programmer, skilled in PHP, that can help him growing fast to achieve his MVP. About me, I have still to find my way.</p>
Perchè Node.JS è così difficile da imparare (per chi viene da Java)
2015-10-06T19:00:12Z
https://michelenasti.com/2015/10/perche-node-js-e-cosi-difficile-da-imparare-per-chi-viene-da-java/
<p>Breve riassunto della mia vita programmativa: all'università ho studiato C (male) e Java (bene), con il tempo mi sono specializzato su Java e comunque sui linguaggi orientati ad oggetti. Ho odiato il mondo del web perché, quando 10 anni fa ho sviluppato qualcosina, mi sentivo frustrato che tutto si vedeva male a seconda del browser. Quindi ho snobbato Javascript. Poi invece il mondo Enterprise ha smesso di fare applicazioni standalone e mi sono trovato a dover rimparare html, css e js in meno di tre anni. Ed eccomi qui!</p>
<p>Nel 2005 Javascript non era uno standard, ogni browser lo interpretava a modo suo e veniva usato principalmente per assicurarsi che l'input dei form fosse corretto. Qualcuno provava a sviluppare animazioni e poco più, ma si scontrava contro il mondo frammentato dei browser. Il 2005 era ancora l'anno di Internet Explorer 5.5, Netscape Navigator, Mozilla aveva appena lanciato Firefox, Chrome non esisteva: la browser war aveva superato il punto più critico ma restavano ancora nodi aperti. Per fortuna JQuery ci mise una pezza, e il W3C decise di standardizzare tutto lo stack del frontend per evitare che ogni browser implementasse le cose a modo suo.</p>
<p>10 anni dopo Javascript non è più il linguaggio dei Browser. Javascript è ovunque. Addirittura esistono DB che prendono in input codice JS e lo eseguono sui dati! Tranquilli, non vi parlerò di questo mondo incasinato (cit. il mio amico TilT "se qualcosa si può fare con Javascript, eventualmente verrà fatto in javascript"), non ancora almeno. Questo articolo vorrebbe semplicemente parlarvi di cosa c'è di diverso tra un linguaggio ad oggetti, come Java, e un linguaggio funzionale, come Javascript. E poi parliamo di Node.</p>
<h3>1. Tutto è una funzione</h3>
<p>Ci ho messo un po' a capire questa proprietà dei linguaggi funzionali. In pratica tra i vari tipi di default di un linguaggio funzionale come javascript ci sono int, long, double, Object, ... e <strong>function</strong>. Altri linguaggi, come Scala, usano un approccio simile. Essendo una funzione un tipo assegnabile a una variabile, si possono passare funzioni in input a un'altra funzione, si possono assegnare funzioni alle variabili, e si possono anche scrivere funzioni che ritornano funzioni! è un mondo incasinato ma proverò a spiegarvelo in pochi semplici snippet.</p>
<p>Possiamo assegnare una funzione a una variabile:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> <span class="token function-variable function">saluta</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"ciao"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span></code></pre>
<p>possiamo passare una funzione in input a un'altra funzione:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> <span class="token function-variable function">salutaColCiao</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token string">"ciao"</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token comment">//nota: sto passando la funzione come un argomento qualsiasi!</span><br /><span class="token keyword">var</span> <span class="token function-variable function">salutaConNome</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">saluto<span class="token punctuation">,</span> nome</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">//per come è fatto javascript, se saluto() non è una funzione </span><br /> <span class="token comment">//verrà lanciata un'eccezione</span><br /> <span class="token function">saluto</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">" "</span> <span class="token operator">+</span> nome<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//scriverà "ciao Michele" </span><br /><span class="token function">salutaConNome</span><span class="token punctuation">(</span>salutaColCiao <span class="token punctuation">,</span> <span class="token string">"Michele"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre>
<h3>2. Callback</h3>
<p>Una Callback è una funzione che viene "chiamata dopo". Ad esempio, fate una query al DB, quando questa finisce processate i dati. In un linguaggio come Java scrivereste qualcosa così:</p>
<pre class="language-java"><code class="language-java"><span class="token comment">//codice JAVA</span><br /><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /><span class="token class-name">List</span><span class="token operator">&</span>lt<span class="token punctuation">;</span><span class="token class-name">Paziente</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> pazienteList <span class="token operator">=</span> <span class="token class-name">PazienteDAO</span><span class="token punctuation">.</span><span class="token function">getAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token function">processPazienti</span><span class="token punctuation">(</span>pazienteList<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <br /></code></pre>
<p>E se l'operazione successiva la dovremmo decidere a runtime, in base al punto in cui ci troviamo dell'applicazione? Sempre in JAVA ci conviene scrivere un'interfaccia e passarla al metodo getAll ....</p>
<pre class="language-java"><code class="language-java"><span class="token comment">//codice JAVA</span><br /><br /><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Callback</span> <span class="token punctuation">{</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">executeOperation</span><span class="token punctuation">(</span><span class="token class-name">Object</span> result<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PazienteDAO</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">DB</span> db<span class="token punctuation">;</span> <br /><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">getAll</span><span class="token punctuation">(</span><span class="token class-name">Callback</span> callback<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">//operazioni di lettura sul db... </span><br /> <span class="token class-name">List</span><span class="token operator">&</span>lt<span class="token punctuation">;</span><span class="token class-name">Paziente</span><span class="token operator">&</span>gt<span class="token punctuation">;</span> pazienteList <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">executeQuery</span><span class="token punctuation">(</span><span class="token string">"select * from paziente"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token comment">//quando ho fatto chiamo la callback: </span><br /> callback<span class="token punctuation">.</span><span class="token function">executeOperation</span><span class="token punctuation">(</span>pazienteList<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Adesso, siccome getAll non restituisce più nulla, per elaborare il dato in uscita dobbiamo implementare la funzione executeOperation per processare l'output:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">...</span> <span class="token comment">// codice JAVA </span><br />pazienteDAO<span class="token punctuation">.</span><span class="token function">getAll</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <br /> @Override<br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">executeOperation</span><span class="token punctuation">(</span><span class="token parameter">Object result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> List<span class="token operator">&</span>lt<span class="token punctuation">;</span>Paziente<span class="token operator">&</span>gt<span class="token punctuation">;</span> pazienteList <span class="token operator">=</span> <span class="token punctuation">(</span>List<span class="token operator">&</span>lt<span class="token punctuation">;</span>Paziente<span class="token operator">&</span>gt<span class="token punctuation">;</span><span class="token punctuation">)</span> result<span class="token punctuation">;</span> <br /> <span class="token function">process</span><span class="token punctuation">(</span>pazienteList<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><br /><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Ecco, questa è una callback in Java. In JS è moooolto più semplice, visto che si possono passare le funzioni e quindi non dobbiamo scrivere codice in più (bloat-code):</p>
<pre class="language-java"><code class="language-java">patientDAO<span class="token punctuation">.</span><span class="token function">getAll</span><span class="token punctuation">(</span><span class="token function">function</span><span class="token punctuation">(</span>patientList<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">//tutte le operazioni che servono </span><br /> <span class="token function">process</span><span class="token punctuation">(</span>patientList<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>O ancora più semplicemente:</p>
<pre class="language-javascript"><code class="language-javascript">patientDAO<span class="token punctuation">.</span><span class="token function">getAll</span><span class="token punctuation">(</span><span class="token function">process</span><span class="token punctuation">(</span>patientList<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3>3. tutto è in un solo thread (e basta!)</h3>
<p>Questa è difficile da spiegare, perchè non è davvero così. Il thread che analizza il codice javascript e lo traduce in codice macchina è uno solo. Ma tutte le operazione di sistema, come quelle di I/O (su disco, su rete...) vengono eseguite su un thread a parte. L'idea è che, quando chiamiamo un'operazione di IO come una lettura su disco, il sistema operativo si occupa di leggere il file e lo fa in maniera <em>asincrona</em>, così che Node può passare subito all'istruzione successiva. Quando il sistema operativo ha terminato la lettura del file chiama la callback, che viene schedulata da Node per essere eseguita nel famoso unico thread.</p>
<p><img src="https://michelenasti.com/uploads/2015/10/threading_node.png" alt="una spiegazione visibile di come funziona l'event loop di javascript" /></p>
<p>E la scalabilità? L'approccio asincrono permette di gestire migliaia di richieste contemporanee con un singolo thread (Provate a farlo fare ad Apache!).</p>
<p>E l'octacore che ho comprato a Natale? Programmando con un paradigma "funzionale" si impara anche a programmare in maniera stateless, dunque si possono lanciare più istanze di Node e mettere un load-balancer che inoltra le richieste ai vari processi.</p>
<p>Per ricapitolare: <em>tutto ciò che fa parte di una libreria base viene eseguito in parallelo, <strong>tranne il tuo codice</strong></em>.</p>
<h3>4. Il fantastico mondo degli oggetti in JS</h3>
<p>Questa non è troppo difficile da capire ma l'ho messa qui perchè, rispetto ai vari linguaggioni business etc, penso che sia una cosa fantastica. Per creare un oggetto in JS è sufficiente questo :</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> human <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">//oggetto istanziato ma vuoto</span></code></pre>
<p>se voglio creare una property all'interno dell'oggetto posso fare così:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> human <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">height</span><span class="token operator">:</span> <span class="token number">170</span><span class="token punctuation">,</span> <br /> <span class="token literal-property property">weight</span><span class="token operator">:</span> <span class="token number">80</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>e ora potrete accedere alle varie proprietà dell'oggetto con <code>human.weight</code> o <code>human.height</code>.</p>
<p>Potete mettere le funzioni negli oggetti nello stesso modo delle proprietà:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> human <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">height</span><span class="token operator">:</span> <span class="token number">170</span><span class="token punctuation">,</span> <br /> <span class="token literal-property property">weight</span><span class="token operator">:</span> <span class="token number">80</span><span class="token punctuation">,</span> <br /> <span class="token function-variable function">calculateDensity</span> <span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">height<span class="token punctuation">,</span> weight</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> height<span class="token operator">/</span>weight<span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>La cosa da capire è che la funzione così definita non va ad agire sulle property dell'oggetto (perchè è stateless!) ma bisogna passargliele di volta in volta.</p>
<p>Ciò significa che se istanzio 100 human avrò 100 funzioni istanziate? SI. Ed è qui che entrano in gioco i Prototype (ossia, istanzio la funzione una volta, e me la ritrovo sempre). Però non ne voglio parlare in questo articolo perchè diventerebbe un casino. Sappiate solo che ogni oggetto in js può essere liberamente modificato successivamente, quindi posso aggiungere property a human facendo cose tipo <code>human.numberOfEars = 2</code> , o cancellare property esistenti con <code>delete human.height</code> .</p>
<h3>Concludendo</h3>
<p>Javascript gode della fama storica di essere un linguaggio per giochetti. Mentre la gente era impegnata a snobbarlo, alcuni programmatori hanno tirato fuori le webapp più incredibili mai pensate. NodeJS è ciò che restituisce la dignità a JS come un linguaggio vero e proprio: finalmente si possono creare file, aprire socket, etc. etc. etc.</p>
<p>Io stesso sono ancora in fase di studio, e non ho realizzato ancora nulla che sia degno di nota con questo linguaggio. Come prova del 9, per vedere se ho capito i concetti che io stesso ho spiegato, vorrei realizzare una piccola webapp che fa "qualcosa" (idee? suggerimenti?)</p>
<p>Vale la pena studiarlo? SI. Solo il cambio di paradigma (da oggetti a funzionale) ne vale la pena. Sarà un'ottima palestra per tutti quei linguaggi che si dichiarano "funzionali" e che fanno della scalabilità il loro punto di forza.</p>
Follow me on Twitter and Linkedin!
2015-10-09T18:45:20Z
https://michelenasti.com/2015/10/follow-me-on-twitter-and-linkedin/
<p>Hi people, this is an info post!</p>
<p>I believe that if you are interested in tech, programming, computer science and more, you really SHOULD follow my <a href="https://twitter.com/micnasti">twitter</a> and <a href="https://it.linkedin.com/in/michelenasti">LinkedIn</a> profiles.</p>
<p>(the disadvantage is, sometimes I say my opinion 🙂 )</p>
<p>You can get a preview of what is my twitter timeline in the sidebar of my blog.</p>
<p>Do it! you won't regret.</p>
<p><img src="https://michelenasti.com/uploads/2015/10/follow_the_white_rabbit-968734.jpg" alt="" /></p>
Libreria del giorno: imbroglia i server di Continuous Automation con Wolkswagen
2015-10-13T18:25:17Z
https://michelenasti.com/2015/10/libreria-del-giorno-imbroglia-i-server-di-continuous-automation-con-wolkswagen/
<p>Ogni tanto il mondo diventa un posto migliore, e lo diventa grazie a persone come quelle che pubblicano progetti come <a href="https://github.com/auchenberg/volkswagen">Wolkswagen</a>.</p>
<p>Ma come? Un progetto che si chiama come la nota casa automobilistica, recentemente coinvolta in uno scandalo sui test truccati?</p>
<p>Ebbene si, e per motivi _assolutamente indipendenti_da quelli scritti al paragrafo precedente, <a href="https://github.com/auchenberg/volkswagen">Wolkswagen</a> è una libreria che si accorge che stai eseguendo test su un server CI e li fa passare tutti.</p>
<p>Cito dalla pagina ufficiale del progetto: "Questo ti permetterà di spendere <em>meno</em> tempo a preoccuparti del testing e <em>più</em> tempo a goderti la vita come sviluppatore software affidabile".</p>
<p>Cosa dovevo fare oggi? Ah niente, testing... <code>require("volkswagen")</code> e ho tutto il pomeriggio libero 😉</p>
Slide del mio mini-talk su NodeJS al Linux Day Salerno 2015
2015-10-23T18:34:12Z
https://michelenasti.com/2015/10/slide-del-mio-mini-talk-su-nodejs-al-linux-day-salerno-2015/
<p>Ecco le slide del mio talk al Linux Day Salerno!</p>
<p><!-- iframe plugin v.4.3 wordpress.org/plugins/iframe/ --></p>
<p>Da vero _nerd _vi dirò che potete clonarlo/forkarlo da <a href="https://github.com/musikele/nodejs_linux_day_salerno_2015">github</a>.</p>
<p>In alternativa potete <a href="http://michelenasti.com/uploads/2015/10/nodejs_linux_day_salerno_2015.zip">scaricare le slide in formato zip.</a> Dopodichè vi basterà aprire il file index.html .</p>
<p>Buona serata!</p>
My problem: start custom js code with ngRoute
2015-10-30T08:26:03Z
https://michelenasti.com/2015/10/start-custom-js-with-ngroute/
<p>The problem is: I took a theme for a website and now I am <em>angularizing</em> it, mainly to use two features of this framework: the <strong>ngImport</strong> feature (for the footer) and <strong>ngRoute</strong>, to get a dynamic menu for my website.</p>
<p>After 2-3 hours of playing, I almost gave up.</p>
<p>This template is built in a very classical way, so all js libraries are just JS files that are imported in each page. We are talking of libraries like jquery, parallax, and others.</p>
<p>My "angularization" of the website started like this: from the home page, divide the toolbar (it will be my index) from the content.</p>
<p>At this step I already had problems: jquery was not firing its events on the content page.</p>
<p><strong>BEFORE</strong> the page loaded, then scripts started and jquery found its way to start its functions on the elements. <strong>NOW</strong> the header loads, then all the scripts load, then the content loads, then... Nothing. Seems that i can execute only Angularjs code at this point.</p>
<p>I managed to solve creating a function that wraps the jquery code and calling it everytime I change page; but maybe I have to do this for every other library? Is there a more generic way?</p>
<p><strong>RequireJs</strong> might be the solution and I am exploring it, however I am also concerned with how many libraries I am injecting in my website: right now we are already using 190 Mb of RAM... Too much for a simple website IMHO.</p>
<p>Let me get back to my dirty website... My feeling is that <strong>Angular was too much to get just this simple routing capability,</strong> maybe I have to explore other libraries to get what I want. Any other idea/solution will be very much appreciated!</p>
Angularizing a jQuery website template: what I discovered, what I have done
2015-11-04T13:30:55Z
https://michelenasti.com/2015/11/angularizing-a-jquery-website-template-what-i-discovered-what-i-have-done/
<p>In my <a href="http://michelenasti.com/2015/10/start-custom-js-with-ngroute/">last post</a> I talked about angularizing a jQuery template for a new website I am developing.</p>
<p>Let's remember my problem. The template I have chosen has a jQuery function that starts it's events with <code>$(document).ready(...)</code>. When the homepage (that consists only of the header buttons of the page!) starts, angular is loaded. at this point, before ngRoute decides what to do, the "ready" jQuery event is fired; when the partial.html is finally loaded (for example, the about.html page) no jQuery is fired.</p>
<p>My solution:</p>
<ol>
<li>change the jQuery function in <code>$(document).ajaxComplete(...)</code>. This way, everytime you get an ajax call, the code is re-executed. But... how can jQuery understand if an ajax call has been executed? expecially if it is not fired by jQuery?</li>
<li>in every controller of every page (I only have 3 fortunately), I do a "fake" ajax call that calls an empty file that I have on my server: something like <code>jQuery.ajax( 'fakeFile.tmp' )</code></li>
</ol>
<p>This way jQuery could understand to re-execute the code inside the 'ajaxComplete' event. And this could be done inside AngularJs.</p>
<h3>Is this the right way to do this?</h3>
<p>Well, no. Usually if you start with angular, you should stick with Angular. If you start with jQuery, stick with it. But in this case I had to develop a website starting from a well-done _jQuery_ed template. And I wanted to use some cool features that I can easily implement in Angular - translation, routing of pages, import of html fragments. So, <em>in this particular case</em> I feel proud of my solution: little study of the situation gave me the best effect with little code.</p>
<h3>What options were you exploring?</h3>
<p>In my last post I was suggesting to myself to use requireJs to lazy load the dependencies every time we change page. Unfortunately this is not the right approach. At the end I did not do this.</p>
<p>In the meantime, I asked some friends about my problem and they suggested me to NOT use requireJs; since I am using angular, a better idea might be to use <a href="https://oclazyload.readme.io/">ocLazyLoad</a>. The advantage is that it can be completely integrated in an Angular App at whatever level you like - in the router, in the controller, etc etc. However I don't need this too 😉</p>
Unofficial but simple way to upgrade Moodle
2015-11-12T09:09:18Z
https://michelenasti.com/2015/11/unofficial-but-simple-way-to-upgrade-moodle/
<p>In the last days I have been involved in setting up a Moodle website. Moodle is a very complex software that wights 140+ MB, about 14000+ files. Installing it is difficoult, Upgrading it is a pain. Why?</p>
<p>Compared to other php applications, like WordPress, Moodle is not so automatized. Installing it means to creating a folder outside the http root of your server, creating a database and setting up a cron job. WordPress does not need this and its installation (and backup) (and upgrade) is totally automatic!</p>
<p>Recently I also received an attack on my moodle server that caused me some big trouble; all hostings were turning off my account asking me to repair it.</p>
<h4>Upgrading Moodle: how I do it</h4>
<p>I am using a private italian hosting, and I am not able to access the server via ssh so I have to upgrade via ftp.</p>
<p>First, unnamed step:</p>
<ul>
<li>did you backup? (I pay the hosting for a weekly backup, for now this is good!)</li>
</ul>
<p>then, my checklist:</p>
<ol>
<li>upload latest moodle.zip.</li>
<li>maintenance mode.</li>
<li>access via ftp with Filezilla.</li>
<li>create a new folder called "moodle_old".</li>
<li>copy all content of http root into moodle_old.</li>
<li>uncompress latest moodle.zip. You get a /moodle directory.</li>
<li>move the content of /moodle in the http root.</li>
<li>move config.php and custom plugins from /moodle_old to the http root.</li>
<li>reach out your website with a browser and do the DB upgrade, upgrade plugins, etc etc (from this point you can follow instructions in browser).</li>
</ol>
<p>Et voilà! Moodle is updated. 9 steps is still a lot tough. There are many steps that you can do wrong and loose all your files. I wonder Moodle was easier to upgrade, but the truth is that it's one of the most cusotmized software in the planet - what if an automatic upgrade deletes a university customization?</p>
<p>You get the point. Happy upgrading.</p>
React.js day a Napoli: impressioni
2015-11-17T13:30:16Z
https://michelenasti.com/2015/11/react-js-day-a-napoli-impressioni/
<p>Hi all,</p>
<p>Ieri ho partecipato al primo <strong>evento su React</strong> organizzato a Napoli, nella sede di Rework. I due speaker <strong><a href="http://esposi.to/">Gianluca Esposito</a></strong> e <strong><a href="https://www.linkedin.com/in/davidecerbo">Davide Cerbo</a></strong> sono stati due miei colleghi e, oltre a salutarli, sono andato anche a sentire un po' di dissertazioni sul tema della UI <em>the-react-way</em>.</p>
<p><img src="https://michelenasti.com/uploads/2015/11/wpid-wp-14477441900643.jpg" alt="" /></p>
<p>I framework javascript sono diventati estremamente complessi e non si occupano più di disegnare div sullo schermo ma organizzano completamente il modo di lavorare del developer. Con le loro filosofie molto opinionate, ti insegnano qual è la <em>Strada</em> che un bravo sviluppatore deve seguire e se sbagli paghi pegno. L'ho imparato a mie spese con Angular quindi perché non vedere lo stesso con React?</p>
<p>Un'altra considerazione che faccio è che passo tutto il giorno a lavoro (8 ore! Un'eternità) e sebbene possa imparare queste cose "da me" a casa, avere una persona che ha già vissuto i problemi del novizio e che si è già posto le domande prima di me è di un aiuto incredibile. Questi eventi sono dunque un ottimo punto di inizio. Ora potrei iniziare a sperimentare con React in maniera più coscienziosa!</p>
<h3>Qual è l'obiettivo di React?</h3>
<p>React si propone solo di definire la view della UI, quindi tutto il resto (la gestione di Model e Controller) é a carico di altri tool; sempre Facebook ha proposto Flux per questo quindi la combo vincente sembra essere React+Flux (non secondo Davide che invece propone Reflux).</p>
<p>Un altro aspetto interessante é che, grazie a React Mobile, possiamo programmare le UI delle nostre App native utilizzando gli stessi concetti e sintassi. Inoltre si azzerano i tempi di reload tra una modifica e l'altra. Questo è davvero impressionante: niente più litigi con eclipse / xcode aspettando che ricarichi!</p>
<p>In conclusione? React non é ufficialmente 1.0 ma lo diventerà a breve; nel frattempo molti sviluppatori (e persino qualche azienda) hanno deciso di utilizzarlo e non ne sembrano pentiti. La curva di apprendimento è bassa e la velocità di sviluppo / esecuzione alta. E infine, Facebook (principale sponsor e sviluppatore) lo utilizza quotidianamente sui suoi siti web e nelle sue App. Garanzia che il vostro prossimo progetto vedrà almeno un miliardo di utenti collegati contemporaneamente.</p>
I tre pilastri per migliorare la propria carriera
2015-11-18T18:09:10Z
https://michelenasti.com/2015/11/i-tre-pilastri-per-migliorare-la-propria-carriera/
<p>Ovviamente non parlo secondo me, eh. Ma secondo <strong>Reid Hoffman</strong>, fondatore di LinkedIn, investitore in tante altre startup, e uomo anche un pò fortunello in verità (si é trovato nel posto giusto e al momento giusto più volte nella sua vita).</p>
<p>Hoffman ha scritto un libro, <a href="http://amzn.to/2mstZED">Startup Of You</a> (in italiano: <a href="http://amzn.to/2mssyFV">Teniamoci in contatto</a>), in cui dà suggerimenti su come migliorare la propria carriera adottando gli stessi meccanismi analitici e decisionali che utilizzano le startup.</p>
<p>Dovreste già saperlo, ma le startup sono aziende allo stadio embrionale che implementano un'idea (spesso tecnologica e/o rivoluzionaria) e che puntano a fare tanti soldi, subito.</p>
<p>Chiaramente, non tutte le idee brillanti godranno di soldi sicuri. Anzi, per fare soldi servono soldi, e tutto si frapporrà tra voi e il successo. Diciamo che le probabilità di successo (o di arrivare a 5 anni) sono moooolto basse, ma se ce la fate, so' soldi. Leggete tra le righe: é facile fallire.</p>
<p>Torniamo al libro. Col tempo alcuni analisti hanno sviluppato una serie di <em>euristiche</em> per minimizzare i rischi, basandosi principalmente sui risultati di chi ce l'ha fatta e chi non ce l'ha fatta. Il primo passo da fare per capire come muovere la propria carriera é... Capire a che punto é la propria carriera.</p>
<h3>"Dove mi trovo adesso?"</h3>
<p>I tre pilastri su cui basare l'analisi sono:</p>
<ul>
<li><strong>l'analisi degli asset</strong>, ossia ciò che abbiamo o che già sappiamo fare. Li distinguiamo in <strong>hard assets</strong>, ossia le vostre proprietà (soldi, computer, auto, etc...) e <strong>soft assets</strong>, ciò che sapete fare. Gli <em>hard asset</em> sembrano superflui, ma quando si vuole investire sei mesi della propria vita per apprendere un nuovo skill, avere dei soldi da parte fa comodo. I <em>soft assets</em>, invece, é tutto ciò che non puoi scambiare direttamente per soldi: le abilità, le capacità individuali, etc. Sono sicuramente più importanti perché chi sceglie di pagare il vostro tempo per risolvere i suoi problemi lo fa sulla base dei vostri soft assets. Una parte importante consiste dunque nel saper <em>comunicare efficacemente</em> questi asset agli altri.</li>
<li><strong>aspirazioni e valori</strong>. Le aspirazioni e i valori sono quel che conta davvero per voi nella vita : i desideri più profondi, le idee, gli obiettivi, e la vostra visione del futuro, a prescindere da quel che accade nel mondo esterno. Le aspirazioni sono importanti perché, se lavorate per esse, lavorerete di più e con maggior qualità senza sentirne la fatica.</li>
<li><strong>la realtà del mercato</strong>. É inutile conoscere esotici linguaggi di programmazione se non vi é richiesta. Un prodotto non farà soldi senza clienti disposti a comprarlo, anche se ha un bel design (questa la dovrei spiegare a certi amici...). Contemporaneamente, i vostri asset e le vostre aspirazioni non vi daranno un vantaggio se non c'é un mercato disposto a pagare per loro.</li>
</ul>
<p>Se vi state domandando quali sono i vostri asset, aspirazioni, e il vostro mercato, bene: state facendo un ottimo esercizio per analizzare il vostro futuro. Ricordatevi però che l'analisi deve essere messa in relazione con il <strong>vostro</strong> mondo, non con tutto il mondo. Non dovete essere i migliori chef dell'universo, spesso basta esserlo in città.</p>
<h3>Esercizi</h3>
<p>Provate a scrivere la seguente frase, magari su Linkedin: "grazie a [elenco di capacità, skill, etc] posso fare [tipo di lavoro professionale] meglio di [altri tipi di professionisti nel mio settore]".</p>
<p>Buon lavoro!</p>
Serve un piano, anzi tre
2015-11-20T10:30:00Z
https://michelenasti.com/2015/11/serve-un-piano/
<p>Continuiamo a <a href="http://michelenasti.com/2015/11/i-tre-pilastri-per-migliorare-la-propria-carriera/">raccontare</a> il libro "Startup Of You", è questa volta passiamo all'azione. Serve un piano.</p>
<p>Molti libri motivazionali iniziano col dire che bisogna avere una visione chiara del proprio futuro, che bisogna avere un obiettivo e lavorare per esso, etc.</p>
<p>La verità é che spesso non sappiamo né quali sono le nostre aspirazioni, né se abbiamo obiettivi che possano durare più di due mesi (io sono così... Sempre indeciso e incerto sul da farsi, almeno professionalmente).</p>
<p>Come muoversi in questo pazzo mondo, dove regna l'incertezza? Le startup in genere pianificano con una metodologia chiamata <strong>ABZ planning</strong>.</p>
<p>ABZ planning significa che nella vita bisogna avere non uno, ma tre piani distinti:</p>
<ul>
<li>il <strong>piano A</strong> é quello che state già perseguendo (e che forse non vi piace), ossia il vostro attuale lavoro, il vostro ruolo, etc.</li>
<li>il <strong>piano B</strong> é invece il piano "alternativo" che vi costruirete nel corso della vita. Questo può essere fatto da occasioni che si possono presentare, o da strade completamente costruite da voi (mettersi in proprio?!). Se poi a un certo punto della vita accettate il piano B, questo diventa il vostro nuovo piano A e ricomincia il ciclo.</li>
<li>il <strong>piano Z</strong> sembra essere il piano col nome più figo, ma in realtà é l'<em>ultima spiaggia</em>. Il piano Z é quello da attuare quando tutto va storto, ossia se i piani A e B vanno male. Per un ragazzino di 20 anni può essere di tornare a vivere coi genitori; per un 40enne sposato può essere il rivolgersi alla cassa integrazione... Etc.</li>
</ul>
<h3>Quanto sono flessibili questi piani?</h3>
<p>Molto. Siccome nessuno vi sta puntando una pistola alla testa intimandovi di rispettare ciò che avevate deciso 2-3 anni fa, voi potete valutare diversi piani B nel corso della vostra vita. Il libro stesso cita gli esempi di Flickr, che prima che fosse un'azienda di photo sharing era un videogame; o PayPal, che aveva come focus principalmente gli utenti Palm (ve lo ricordate?!) poi eBay ha cambiato tutto. Quindi come vedete anche il mondo esterno (che é mutevole per definizione) può influire sulle vostre scelte.</p>
<p>Ora serve solo il coraggio di scegliere e affrontare le conseguenze. Forza!</p>
di ritorno dal Codemotion Milan 2015
2015-11-24T19:30:36Z
https://michelenasti.com/2015/11/ritorno-dal-codemotion-milan-2015/
<p>Ed eccomi di ritorno dal <a href="http://milan2015.codemotionworld.com/">secondo Codemotion</a> dell'anno! Questa volta sono stato a Milano, dove ho seguito talk interessanti purtroppo solo per il giorno di sabato. Qui potete trovare altri articoli sul <a href="http://michelenasti.com/2015/01/codemotion-2015/">Codemotion di Roma</a>.</p>
<p>Tralasciamo il disclaimer che do sempre, ossia che é importante andare alle conferenze perché si "annusano" i trend di oggi e di domani, e che comunque se non volete andare alle conferenze almeno aggiornatevi, perché tra 3-5 anni tutto sarà cambiato e voi... Meglio che non siate sempre gli stessi.</p>
<p>Parliamo del talk che mi é piaciuto di più : <a href="https://github.com/uber/ringpop-node"><strong>Ringpop</strong></a>, un modulo lato server scritto in Node e Go da quei pazzi di Uber. Fondamentalmente é un sistema distribuito per scrivere codice lato server, in modo che sia scalabile, fault tolerant e sempre aggiornato. Ringpop si preoccupa di replicare i vari moduli del server e fa in modo che se uno di questi casca, gli altri sono capaci di accorgersene e possono anche riattivarlo. <em>Fiiigo</em>.</p>
<p>Un altro talk degno di nota é quello di <a href="https://it.linkedin.com/in/mario-fusco-3467213"><strong>Mario Fusco</strong></a> sui <strong>paradigmi di programmazione concorrente per la JVM</strong>: secondo Mario l'unico paradigma che conosciamo tutti é quello basato sui thread, perché lo abbiamo studiato all'Università, ma é anche quello più inadeguato perché é così di basso livello che é facile che tutto vada in lock. E allora Mario proponeva una serie di paradigmi, che non cito tutti perché non ce li ho qui a portata di mano, ma comunque ne esistono almeno 5. Io ricordo il modello ad <a href="http://doc.akka.io/docs/akka/snapshot/scala/actors.html">Attori</a>, oppure <a href="https://github.com/ReactiveX/RxJava">RxJava</a>, oppure i parallel streams con l'approccio funzionale introdotto in Java 8.</p>
<p>Il mio amico <a href="http://esposi.to/">Gianluca Esposito</a> ha poi presentato un talk su <a href="https://facebook.github.io/react-native/"><strong>React Native</strong></a>, un framework che sta ottenendo un discreto successo di questi tempi. React di per sé é una libreria per creare la view di una webapp, e le sue prestazioni e il modello concettuale sono stati di sicuro l'innovazione frontend dell'anno, così come questo è stato l'anno della consacrazione per Angular.</p>
<p>Due parole su React Native, e del perché é meglio di Ionic/Phonegap/you-name-it : una volta imparato React si utilizzano gli stessi concetti (non si deve imparare nulla di nuovo) per creare <strong>webapp native</strong> che non girano in una webview, anzi i vari blocchi vengono renderizzati come componenti nativi veri dunque massima performance. E anche lo sviluppo é facilitato dal live reload che la piattaforma offre.</p>
<p>Altro talk degni di nota: "da Angular a React" (da cui ho preso l'immagine che vedete allegata), in cui lo speaker parlava principalmente delle difficoltà concettuali che ha avuto lui nel capire React, soprattutto quando vieni da un framework all-inclusive come Angular.</p>
<p>E ora? Aspettiamo con ansia Codemotion Rome 2016!</p>
Tutti i modi per generare ID in Hibernate & JPA
2015-11-27T10:00:50Z
https://michelenasti.com/2015/11/tutti-modi-generare-id-hibernate-jpa/
<p>Siccome dovevo creare ID con alcune proprietà particolari per alcuni oggetti, ho iniziato a cercare tutti i vari modi in cui si potevano assegnare ID a oggetti JPA e Hibernate.</p>
<p>Avevo questo articolo nelle bozze da un bel po'; fondamentalmente sono un programmatore Full-Stack ma, quando una cosa non ti piace, tendi a non memorizzarla 🙂 quindi mi segno molte cose che faccio qui, sul blog, che magari poi diventano articoli.</p>
<h2>JPA</h2>
<p>(vedi <a href="https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing">https://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing</a>)</p>
<ul>
<li><strong>table sequencing</strong>, ossia prende il prossimo ID da una tabella appositamente configurata sul proprio DB. Portabile, ma lento. Esempio:</li>
</ul>
<pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Entity</span><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Employee</span> <span class="token punctuation">{</span><br /> <span class="token annotation punctuation">@Id</span><br /> <span class="token annotation punctuation">@TableGenerator</span><span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"TABLE_GEN"</span><span class="token punctuation">,</span> table<span class="token operator">=</span><span class="token string">"SEQUENCE_TABLE"</span><span class="token punctuation">,</span> pkColumnName<span class="token operator">=</span><span class="token string">"SEQ_NAME"</span><span class="token punctuation">,</span><br /> valueColumnName<span class="token operator">=</span><span class="token string">"SEQ_COUNT"</span><span class="token punctuation">,</span> pkColumnValue<span class="token operator">=</span><span class="token string">"EMP_SEQ"</span><span class="token punctuation">)</span><br /> <span class="token annotation punctuation">@GeneratedValue</span><span class="token punctuation">(</span>strategy<span class="token operator">=</span><span class="token class-name">GenerationType</span><span class="token punctuation">.</span><span class="token constant">TABLE</span><span class="token punctuation">,</span> generator<span class="token operator">=</span><span class="token string">"TABLE_GEN"</span><span class="token punctuation">)</span><br /> <span class="token keyword">private</span> <span class="token keyword">long</span> id<span class="token punctuation">;</span><br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /><span class="token punctuation">}</span></code></pre>
<ul>
<li><strong>Sequence objects</strong>, usati solo sui DB che li supportano, tipo Oracle. Sono degli oggetti che contengono il prossimo ID da utilizzare. Funziona bene ma non è portabile (MySql non ce l'ha).</li>
</ul>
<pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Entity</span><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Employee</span> <span class="token punctuation">{</span><br /> <span class="token annotation punctuation">@Id</span><br /> <span class="token annotation punctuation">@GeneratedValue</span><span class="token punctuation">(</span>strategy<span class="token operator">=</span><span class="token class-name">GenerationType</span><span class="token punctuation">.</span><span class="token constant">SEQUENCE</span><span class="token punctuation">,</span> generator<span class="token operator">=</span><span class="token string">"EMP_SEQ"</span><span class="token punctuation">)</span><br /> <span class="token annotation punctuation">@SequenceGenerator</span><span class="token punctuation">(</span>name<span class="token operator">=</span><span class="token string">"EMP_SEQ"</span><span class="token punctuation">,</span> sequenceName<span class="token operator">=</span><span class="token string">"EMP_SEQ"</span><span class="token punctuation">,</span> allocationSize<span class="token operator">=</span><span class="token number">100</span><span class="token punctuation">)</span><br /> <span class="token keyword">private</span> <span class="token keyword">long</span> id<span class="token punctuation">;</span><br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /><span class="token punctuation">}</span></code></pre>
<ul>
<li><strong>Identity sequencing</strong>, incrementa di 1 l'ID ad ogni passo. Dipende dal DB purtroppo, ad esempio Oracle non lo supporta.</li>
</ul>
<pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Entity</span><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Employee</span> <span class="token punctuation">{</span><br /> <span class="token annotation punctuation">@Id</span><br /> <span class="token annotation punctuation">@GeneratedValue</span><span class="token punctuation">(</span>strategy<span class="token operator">=</span><span class="token class-name">GenerationType</span><span class="token punctuation">.</span><span class="token constant">IDENTITY</span><span class="token punctuation">)</span><br /> <span class="token keyword">private</span> <span class="token keyword">long</span> id<span class="token punctuation">;</span><br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /><span class="token punctuation">}</span></code></pre>
<h2>HIBERNATE</h2>
<p>Con Hibernate vi sono altre alternative possibili, alcune sono piccole variazioni di quelle viste in precedenza (e vi consiglio di utilizzare JPA piuttosto che legarvi a Hibernate). Quasi sempre, al posto di SequenceGenerator useremo GenericGenerator che è proprio di Hibernate.</p>
<ul>
<li>
<p><strong>HiLo</strong> : un algoritmo per generare ID numerici (Long, Int) che fa poche chiamate al DB per conoscere la sequenza corrente; se due transazioni concorrenti provano ad avere lo stesso ID, allora otterranno due numeri profondamente diversi. <a href="http://vladmihalcea.com/2014/06/23/the-hilo-algorithm/">Esempio di uso</a>:</p>
<ul>
<li>Se la chiave è di tipo Stringa, si può usare un <strong>generatore di UUID</strong> :</li>
</ul>
</li>
</ul>
<pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Id</span><br /><span class="token annotation punctuation">@GeneratedValue</span><span class="token punctuation">(</span>generator <span class="token operator">=</span> <span class="token string">"system-uuid"</span><span class="token punctuation">)</span><br /><span class="token annotation punctuation">@GenericGenerator</span><span class="token punctuation">(</span>name <span class="token operator">=</span> <span class="token string">"system-uuid"</span><span class="token punctuation">,</span> strategy <span class="token operator">=</span> <span class="token string">"uuid"</span><span class="token punctuation">)</span><br /><span class="token keyword">private</span> <span class="token class-name">String</span> id<span class="token punctuation">;</span></code></pre>
<ul>
<li>"<strong>assigned</strong>": il programmatore si deve preoccupare di generare un ID (univoco) prima di memorizzare l'entità</li>
<li>"<strong>increment</strong>": in Hibernate equivale sempre a max(id) +1 (diverso da JPA!)</li>
<li><strong>custom id generator</strong>: questo sembra essere ciò che mi serve ma <a href="http://learningviacode.blogspot.it/2011/11/creating-custom-id-generator.html?showComment=1436277811662#c1961377562249766537">l'articolo</a> risale al 2011 e la configurazione è descritta in xml. Devo trovare la versione con le annotazioni. Su <a href="http://stackoverflow.com/questions/11631800/hibernate-how-specify-custom-sequence-generator-class-name-using-annotations">stack overflow</a> c'è una versione che utilizza le annotations.</li>
</ul>
Leggere la query Hibernate in debug
2015-11-28T10:02:43Z
https://michelenasti.com/2015/11/459/
<p>vi capita mai di aver disabilitato le query Hibernate/Jpa in Tomcat ? Ossia, le query non vengono stampate nella console. In genere si disabilitano altrimenti le prestazioni dell'app diventano scarsissime.</p>
<p>Con Intellij possiamo metterci in debug nel DAO e premere Ctrl+U, poi su Evaluate Code e incolliamo il papiello:</p>
<p><pre class="lang:default decode:true">CriteriaImpl criteriaImpl = (CriteriaImpl)criteria;
SessionImplementor session = criteriaImpl.getSession();
SessionFactoryImplementor factory = session.getFactory();
CriteriaQueryTranslator translator=new CriteriaQueryTranslator(factory,criteriaImpl,criteriaImpl.getEntityOrClassName(),CriteriaQueryTranslator.ROOT_SQL_ALIAS);
String[] implementors = factory.getImplementors( criteriaImpl.getEntityOrClassName() );</p>
<p>CriteriaJoinWalker walker = new CriteriaJoinWalker((OuterJoinLoadable)factory.getEntityPersister(implementors[0]),
translator,
factory,
criteriaImpl,
criteriaImpl.getEntityOrClassName(),
session.getLoadQueryInfluencers() );</p>
<p>String sql=walker.getSQLString();</pre></p>
<p>Una volta cliccato su "evaluate", vedrete la query sql che viene mandata al db. Unico problema: non vengono stampati i parametri ...</p>
<p>D'obbligo citare <a href="http://stackoverflow.com/questions/554481/how-to-get-sql-from-hibernate-criteria-api-not-for-logging">Stack Overflow</a> per questo trick.</p>
Non c'é nulla di più definitivo di una scelta provvisoria
2015-12-11T08:34:51Z
https://michelenasti.com/2015/12/non-ce-nulla-di-piu-definitivo-di-una-scelta-provvisoria/
<p>Negli ultimi due giorni ho lavorato a un refactoring della webapp che stiamo sviluppando e, mentre sistemavamo tutti i problemi che incontravamo lungo la strada, abbiamo trovato tante scelte <em>provvisorie</em> fatte dagli sviluppatori di sei mesi fa (alcune anche di un anno fa). Ci faceva sorridere il fatto che alcuni div fossero addirittura etichettati come <code>provvisorio</code> e invece sono resistiti tutto questo tempo.</p>
<p>Voi lo sapete che meno refactoring si fa sul codice meglio é, tuttavia ogni tanto un refactoring é indispensabile altrimenti i poveri sviluppatori impazziscono 🙂</p>
<p>Questo problema delle scelte provvisorie che poi diventano definitive travalica i confini dell'informatica: mi vengono in mente i <a href="https://it.m.wikipedia.org/wiki/Trullo">Trulli di Alberobello</a>, costruiti senza malta (quindi con la sola pietra) affinché potessero essere facilmente smontabili in caso di ispezione da parte del Regno delle Due Sicilie. Ai tempi si pagava una tassa per ogni nuovo insediamento!</p>
<p>Tornando all'informatica, lo sviluppatore che deve scegliere tra due o più strade ha generalmente due opzioni: farlo bene (e ci metterà del tempo), o farlo subito (col classico <em>pezzotto</em>). Non ci crederete ma, sulla base della mia esperienza, <strong>é il management che spinge verso soluzioni veloci</strong> che non resistono alla prova del tempo! Purtroppo spesso l'ansia di completare un'attività (e di rispettare la tabella di marcia) fa dire ai nostri capi che <em>poi si vede</em>, ignorando i costi a breve e lungo termine.</p>
<p>A breve termine può darsi che ciò che é stato fatto si debba rifare: quando si ha un feedback rapido, come con metodologie Agili, potrebbe accadere proprio questo. E il tempo speso per realizzarlo raddoppia. (c'é da dire che spesso manco il cliente sa cosa vuole, e solo quando vede una soluzione riesca a dire "ma io non volevo questo"!).</p>
<p>A lunghissimo termine invece, non é detto che la nostra soluzione sia sempre rifattorizzabile: spesso il codice potrebbe gestire parti così oscure del programma che toccandolo (o anche solo guardandolo) potrebbe rompersi qualcosa che non di sospettava nemmeno che fosse collegata. E allora che si fa? Un altro pezzotto! Alla lunga questo pregiudica la manutenibilità e la risoluzione dei bug.</p>
<p><em>Infine, ci sono pezzotti che sono veri e propri capolavori. Quando ne ho trovati sono rimasto sorpreso da tanto ingegno (mi viene in mente la variabile <em>pezzott++).</em></em> In quei casi quasi dispiace dover modificare o mettere mano al codice: del resto, <em>se funziona, perché cambiarlo? 🙂</em></p>
Essere bravi colleghi evitando di committare cazzate
2015-12-24T10:30:46Z
https://michelenasti.com/2015/12/essere-bravi-colleghi-evitando-di-committare-cazzate/
<p>Ogni volta che qualcuno committa qualcosa di rotto, gli altri dev del team perdono tempo per capire cosa è successo e come risolvere. **Se io fossi il boss mi assicurerei che i miei sviluppatori seguano i passi che descrivo nell'articolo, altrimenti gli pagherei lo stipendio per perdere tempo riparando danni altrui. **</p>
<p>Ah, e vogliamo parlare di quelli che committano pur sapendo che il sistema non funziona?</p>
<p>Ah, e vogliamo parlare di quelli che committano 1 file alla volta? (Quindi se hanno 12 file in uscita fanno 12 commit?!?!?!?)</p>
<p>Lo so, sto diventando talebano, ma è il prezzo da pagare se si vuole raggiungere la <strong>Purity Of Code (™).</strong></p>
<p>Mi è capitato che vado a sincronizzarmi col mio <strong>VCS</strong> preferito (VCS = <strong>Version Control System</strong>; in pratica stiamo parlando di <em>CVS, SVN, Git,</em> etc.) e il codice appena scaricato non compila.</p>
<p>La colpa è quasi sempre di chi ha committato senza seguire <del>elevatissimi</del> standard di sicurezza (una persona meno calma di me potrebbe infatti picchiare i committer). Infatti io raccomando ai miei colleghi la seguente checklist, che a quanto pare nessuno rispetta:</p>
<ol>
<li>scaricare tutti gli update possibili da SVN</li>
<li>fare maven install - compilare dunque il codice nuovo con le modifiche appena apportate</li>
<li>se almeno la compilazione va, commit.</li>
</ol>
<p>Che ci vuole?</p>
<p>Questi tre semplici passi non ci mettono al riparo da problemi di runtime, ossia quando l'app non parte per problemi di configurazione. Inoltre non funziona con i linguaggi interpretati. Si potrebbe dunque inserire un passo 2.5: <em>far partire il server</em>.</p>
<p>Sventolate questo post in faccia ai vostri colleghi scapestrati e buffoniatevi un po'.</p>
La Programmazione Funzionale con le Funzioni Pure
2016-01-12T10:10:50Z
https://michelenasti.com/2016/01/la-programmazione-funzionale-con-le-funzioni-pure/
<p>Le <strong>Funzioni Pure</strong> sono uno dei pilastri fondamentali della programmazione funzionale. La definizione vera è qualcosa di astruso e non la scriverò perchè la capirebbero in pochi; invece <em>in parole povere</em> è addirittura un concetto semplice.</p>
<p>Una <strong>funzione</strong> si dice <strong>pura</strong> quando il suo <strong>output</strong> <strong>dipende esclusivamente dal suo input.</strong></p>
<p>Sembra una cretinata, ma il 99% del codice scritto ogni giorno nel mondo non rispetta questa regola.</p>
<p>Vediamo un esempio di funzione impura e di funzione pura:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> etaMinima <span class="token operator">=</span> <span class="token number">30</span><span class="token punctuation">;</span> <br /><br /><span class="token comment">//impura</span><br /><span class="token keyword">function</span> <span class="token function">checkEta</span><span class="token punctuation">(</span><span class="token parameter"><span class="token keyword">var</span> eta</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> eta <span class="token operator">>=</span> etaMinima<span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//pura</span><br /><span class="token keyword">function</span> <span class="token function">checkEta</span><span class="token punctuation">(</span><span class="token parameter"><span class="token keyword">var</span> eta</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> etaMinima <span class="token operator">=</span> <span class="token number">30</span><span class="token punctuation">;</span> <br /> <span class="token keyword">return</span> eta <span class="token operator">>=</span> etaMinima<span class="token punctuation">;</span> <br /><span class="token punctuation">}</span></code></pre>
<p>Riuscite a vedere il problema? la prima checkEta dipende da una variabile esterna, che può cambiare in qualunque momento e dunque può influenzare il modo in cui viene eseguito e valutato il codice.</p>
<p>Avete presente la parola chiave <em>this</em> ? Se la usate, o l'avete usata, il vostro codice è impuro. E' possibile trasformare parecchio in codice puro molte funzioni impure, ma per farlo dovrete dimenticare (per un po') il mondo <em>object oriented.</em></p>
<p>Vediamo alcuni <em>effetti collaterali</em> (in matematica diremmo <em>corollari</em>) delle _pure function_s:</p>
<ul>
<li>una pure function <strong>non modifica l'input</strong> in ingresso.</li>
<li>una pure function <strong>può essere riapplicata più volte sullo stesso input e si otterrà sempre lo stesso output.</strong> Questa proprietà viene solitamente detta <em>idempotenza</em>.</li>
<li>Una Pure Function <strong>può chiamare altre Pure Function</strong>. (In object-oriented li chiameremmo metodi statici, ma non tutti, solo quelli che non dipendono da nessun'altro).</li>
<li>Possiamo sfruttare l'ultima proprietà per <em>cacheare</em> il risultato delle pure function tramite una tecnica che si chiama <strong>memoization</strong>:</li>
</ul>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> <span class="token function-variable function">memoize</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">f</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> cache <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> arg_str <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> cache<span class="token punctuation">[</span>arg_str<span class="token punctuation">]</span> <span class="token operator">=</span> cache<span class="token punctuation">[</span>arg_str<span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token function">f</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> arguments<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> cache<span class="token punctuation">[</span>arg_str<span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>e se abbiamo una chiamata Http che è anch'essa idempotente potremmo cachearla con questa tecnica:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> pureHttpCall <span class="token operator">=</span> <span class="token function">memoize</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">url<span class="token punctuation">,</span> params</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <br /> <span class="token keyword">return</span> $<span class="token punctuation">.</span><span class="token function">getJSON</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> params<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Dalla seconda volta in cui verrà chiamata <code>pureHttpCall</code>, il risultato non sarà preso dalla rete ma dalla cache. Ingegnoso, no?</p>
<p>Se volete altri approfondimenti sul paradigma funzionale leggete questo libro free: <a href="https://github.com/MostlyAdequate/mostly-adequate-guide/">Mostly Adequate Guide to Functional Programming</a>. Alcuni spunti (ed esempi) li ho presi dal capitolo 3. Buona lettura!</p>
un pratico esempio del perchè l'Object Oriented non è la Soluzione Universale©'
2016-01-13T10:10:58Z
https://michelenasti.com/2016/01/perche-lobject-oriented-non-e-la-soluzione-a-tutto/
<p>Ieri molti amici e lettori mi hanno chiesto perchè avessi iniziato ad approfondire il paradigma funzionale, quando con l'object oriented riesci a risolvere quasi tutti i problemi del mondo.</p>
<p>Voglio farvi vedere un esempio di codice che, siccome dipende dallo stato interno degli oggetti e questo stato è mutevole, risulta molto difficile da seguire e da analizzare.</p>
<p>Stavolta l'esempio l'ho preparato in Java, il linguaggio OO per eccellenza. Ho creato la classe <code>Stormo</code> (di gabbiani) che modella uno stormo. Ogni stormo ha due metodi, il metodo <code>unisci</code> con un altro stormo, e <code>riproduci</code>, che modella essenzialmente quella cosa là :D.</p>
<p>Nel main invece vedete un po' di operazioni sugli Stormi. Quanto vale result?</p>
<pre class="language-java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">Stormo</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">public</span> <span class="token keyword">int</span> gabbiani<span class="token punctuation">;</span><br /><br /> <span class="token keyword">public</span> <span class="token class-name">Stormo</span><span class="token punctuation">(</span><span class="token keyword">int</span> n<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>gabbiani <span class="token operator">=</span> n<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">public</span> <span class="token class-name">Stormo</span> <span class="token function">unisci</span><span class="token punctuation">(</span><span class="token class-name">Stormo</span> other<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>gabbiani <span class="token operator">+=</span> other<span class="token punctuation">.</span>gabbiani<span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">public</span> <span class="token class-name">Stormo</span> <span class="token function">riproduci</span><span class="token punctuation">(</span><span class="token class-name">Stormo</span> other<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>gabbiani <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>gabbiani <span class="token operator">*</span> other<span class="token punctuation">.</span>gabbiani<span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span> args<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><br /> <span class="token class-name">Stormo</span> stormo_a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stormo</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token class-name">Stormo</span> stormo_b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stormo</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token class-name">Stormo</span> stormo_c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Stormo</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">int</span> result <span class="token operator">=</span> stormo_a<span class="token punctuation">.</span><span class="token function">unisci</span><span class="token punctuation">(</span>stormo_c<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">riproduci</span><span class="token punctuation">(</span>stormo_b<span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">unisci</span><span class="token punctuation">(</span>stormo_a<span class="token punctuation">.</span><span class="token function">riproduci</span><span class="token punctuation">(</span>stormo_b<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>gabbiani<span class="token punctuation">;</span><br /><br /> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Quanti gabbiani contate? Quanto vale <code>result</code> alla fine del Main ?</p>
<p>La risposta che avete contato voi è ... 16. La riposta del compilatore invece è ... 32. E l'oggetto <code>stormo_a</code> è addirittura cambiato!</p>
<p>Come vedete, sono bastate poche righe di codice per ottenere un risultato sballato e un bug piuttosto evidente.</p>
<p>Uno dei problemi che affligge questo codice è che va a mutare lo stato interno dell'oggetto; se i metodi <code>unisci</code> e <code>riproduci</code> avessero restituito copie e lasciato immutato la classe stessa, ora non saremmo qui a parlarne.</p>
<h3>Come si risolve?</h3>
<p>Amici suggeriscono di mostrare cosa si dovrebbe cambiare affinchè il codice funzioni.</p>
<p>Io modificherei i metodi <code>unisci</code> e <code>riproduci</code> per ottenere il risultato corretto:</p>
<pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">Stormo</span> <span class="token function">unisci</span><span class="token punctuation">(</span><span class="token class-name">Stormo</span> other<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Stormo</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>gabbiani<span class="token operator">+</span>other<span class="token punctuation">.</span>gabbiani<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">public</span> <span class="token class-name">Stormo</span> <span class="token function">riproduci</span><span class="token punctuation">(</span><span class="token class-name">Stormo</span> other<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Stormo</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>gabbiani<span class="token operator">*</span>other<span class="token punctuation">.</span>gabbiani<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>Ed è qui che si applica il concetto di <strong>immutabilità</strong>: non ci sono side effects sull'oggetto chiamato e viene restituito un nuovo oggetto contenente le nuove proprietà.</p>
<hr />
<p>Dunque ciò che dicono i miei amici e colleghi è giusto, nel senso che con la programmazione a oggetti (ma anche con la programmazione iterativa) i problemi si risolvono comunque; l'approccio funzionale permette però di avere qualche altro gadget nel coltellino svizzero del programmatore, di scrivere codice più bello, più espressivo, più succinto.</p>
<p>Per completezza, riporto qui l'esempio (con i termini inglesi) in javascript così potrete eseguirlo nella console del browser (senza che aprite eclipse...). Anche questo esempio è preso da <a href="https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch1.md">Mostly Adequate Guide to Functional Programming, capitolo 1.</a></p>
<pre class="language-java"><code class="language-java"><span class="token keyword">var</span> <span class="token class-name">Flock</span> <span class="token operator">=</span> <span class="token function">function</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>seagulls <span class="token operator">=</span> n<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token class-name">Flock</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>conjoin <span class="token operator">=</span> <span class="token function">function</span><span class="token punctuation">(</span>other<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>seagulls <span class="token operator">+=</span> other<span class="token punctuation">.</span>seagulls<span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token class-name">Flock</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>breed <span class="token operator">=</span> <span class="token function">function</span><span class="token punctuation">(</span>other<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>seagulls <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>seagulls <span class="token operator">*</span> other<span class="token punctuation">.</span>seagulls<span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">var</span> flock_a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Flock</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> flock_b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Flock</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> flock_c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Flock</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">var</span> result <span class="token operator">=</span> flock_a<span class="token punctuation">.</span><span class="token function">conjoin</span><span class="token punctuation">(</span>flock_c<span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">breed</span><span class="token punctuation">(</span>flock_b<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">conjoin</span><span class="token punctuation">(</span>flock_a<span class="token punctuation">.</span><span class="token function">breed</span><span class="token punctuation">(</span>flock_b<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>seagulls<span class="token punctuation">;</span><br /><span class="token comment">//=> 32</span></code></pre>
Realizzare un piccolo motore di Scripting in una webapp Java con Groovy
2016-01-22T10:10:35Z
https://michelenasti.com/2016/01/realizzare-un-piccolo-motore-di-scripting-in-una-webapp-java-con-groovy/
<p>Alla Webapp a cui stiamo lavorando abbiamo dovuto integrare un motore di scripting per aiutare gli specialisti che dovranno interfacciarsi con gli strumenti di laboratorio.</p>
<p>Nella precedente versione dell'applicazione, realizzata in Visual Basic, questi script erano realizzati in PL/SQL (perchè c'era bisogno di leggere e scrivere dati sul DB)... ma questa scelta ora non è più praticabile perchè noi vogliamo supportare più DB.</p>
<p>Così tra ieri e oggi mi sono messo a cercare un po' di info su quale fosse il miglior linguaggio scriptabile e integrabile con Java, che avesse le seguenti caratteristiche:</p>
<ul>
<li>deve poter accedere al database e "vivere" nella stessa transazione del chiamante</li>
<li>deve poter chiamare i servizi Java già creati (solitamente creati come bean spring)</li>
<li>i servizi Java devono poter chiamare questi eventuali script</li>
<li>devono poter essere sostituiti "on-the-fly", senza spegnere e accendere il software.</li>
</ul>
<p>Il management aveva individuato <a href="http://www.drools.org/">Drools</a> come uno dei possibili tools che il team già conosce, che non risponde però a tutti i requisiti. Il mio compito era di continuare a investigare per trovare altre alternative.</p>
<h4>let me google "Java Scripting" for you</h4>
<p>Fin dall'inizio ho pensato a Groovy ma da qualche parte ho letto che Spring supporta altri due linguaggi, che ho dovuto scartare:</p>
<ul>
<li><a href="https://github.com/beanshell/beanshell"><strong>BeanShell</strong></a>. Purtroppo i siti web a riguardo non sono molto aggiornati, e sembra che il progetto sia ospitato su github ora. Il sito ufficiale è in disuso dal 2005, github non viene aggiornato da 4 mesi e comunque c'è stato un solo avanzamento di versione in 10 anni. Ci sono dubbi sulla compatibilità con Java8 e, da una mia prova, non riesce a chiamare bean autowired di spring.</li>
<li><a href="http://jruby.org/">JRuby</a>. Ruby è un grande linguaggio e sulla JVM diventa incredibilmente potente, portabile, veloce. Peccato che la sua sintassi sia estremamente complicata per uno <em>scripter</em> alle prime armi.</li>
</ul>
<p>Così siamo arrivati a <a href="http://www.groovy-lang.org/">Groovy</a>. Andate sul sito web se volete leggere le caratteristiche, ma la cosa che ci importava è che</p>
<ul>
<li>supporta una grande parte della sintassi Java (e si possono chiamare classi Java come String, Integer, Boolean...)</li>
<li>è sponsorizzato da SpringSource</li>
<li>esiste da una decina d'anni (e aveva le clojures dalla prima versione!)</li>
<li>Ad oggi è molto utilizzato come framework per lo sviluppo web, ad esempio col framework <a href="https://grails.org/">Grails</a>.</li>
</ul>
<p>Il problema delle transazioni l'ho risolto grazie a <a href="http://sadalage.com/blog/2013/01/14/transactions_using_groovysql/">questo articolo</a>, come potete vedere il data source e il transaction manager vengono definiti in Hibernate e poi passati all'oggetto SQL di Groovy.</p>
<p>La possibilità di <em>Autowirizzare</em> i bean Groovy e di utilizzare i bean Spring è stata un'altra cosa spiegata "abbastanza" bene nel <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/dynamic-language.html">capitolo 34 della guida di Spring</a>.</p>
<h4>Show me the code</h4>
<p>Come prima cosa ho creato un'interfaccia <code>GroovyScripterInterface</code> in JAVA:</p>
<pre class="language-java"><code class="language-java"><span class="token comment">/**<br /> * Created by Michele on 20/01/2016.<br /> */</span><br /><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">GroovyScripterInterface</span> <span class="token punctuation">{</span><br /><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>e una classe Groovy che la implementa:</p>
<p>import groovy.sql.Sql
import org.springframework.beans.factory.annotation.Autowired</p>
<pre class="language-java"><code class="language-java"><span class="token comment">/**<br /> * Created by Michele on 20/01/2016.<br /> */</span><br /><span class="token keyword">class</span> <span class="token class-name">GroovyScripter</span> <span class="token keyword">implements</span> <span class="token class-name">GroovyScripterInterface</span> <span class="token punctuation">{</span><br /><br /> <span class="token annotation punctuation">@Autowired</span> <span class="token class-name">Sql</span> sql<br /><br /> <span class="token annotation punctuation">@Autowired</span> <span class="token class-name">ServizioFarlocco</span> servizioFarlocco<br /><br /> <span class="token keyword">void</span> <span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">printf</span><span class="token punctuation">(</span>'\n\n\n\nHello<span class="token operator">!</span> ' <span class="token operator">+</span> sql <span class="token operator">+</span> <span class="token char">'\n\n\n\n\n'</span><span class="token punctuation">)</span><br /> servizioFarlocco<span class="token punctuation">.</span><span class="token function">mangia</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token keyword">return</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Groovy tradurrà questo codice groovy in una classe Java, e su queste classi si potrà utilizzare AOP, annotazioni, transazioni, etc.</p>
<p><code>ServizioFarlocco</code> invece è una classe Java che non fa nulla di chè.</p>
<p>L'oggetto SQL invece è definito nella configurazione di Hibernate, andate a vedere il primo articolo linkato (quello sulle transazioni in groovy).</p>
<p>E infine ecco l'XML in cui sono definiti i due bean (Java e Groovy):</p>
<pre class="language-xml"><code class="language-xml"><span class="token prolog"><?xml version="1.0" encoding="UTF-8"?></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>beans</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/beans<span class="token punctuation">"</span></span><br /> <span class="token attr-name"><span class="token namespace">xmlns:</span>context</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/context<span class="token punctuation">"</span></span><br /> <span class="token attr-name"><span class="token namespace">xmlns:</span>xsi</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2001/XMLSchema-instance<span class="token punctuation">"</span></span><br /> <span class="token attr-name"><span class="token namespace">xmlns:</span>lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/lang<span class="token punctuation">"</span></span><br /> <span class="token attr-name"><span class="token namespace">xsi:</span>schemaLocation</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.springframework.org/schema/beans<br /> http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br /> http://www.springframework.org/schema/context<br /> http://www.springframework.org/schema/context/spring-context-4.0.xsd<br /> http://www.springframework.org/schema/lang <br /> http://www.springframework.org/schema/lang/spring-lang.xsd<span class="token punctuation">"</span></span><br /><span class="token punctuation">></span></span><br /><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">context:</span>annotation-config</span> <span class="token punctuation">/></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>bean</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>servizioFarlocco<span class="token punctuation">"</span></span> <br /> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>it.eng.areas.eliot.interoperability.ServizioFarlocco<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>bean</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token namespace">lang:</span>groovy</span> <br /> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>groovyScript<span class="token punctuation">"</span></span> <br /> <span class="token attr-name">script-source</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/GroovyScript.groovy<span class="token punctuation">"</span></span> <br /> <span class="token attr-name">refresh-check-delay</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>5000<span class="token punctuation">"</span></span><br /><span class="token punctuation">/></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>beans</span><span class="token punctuation">></span></span></code></pre>
<p>Da notare che il parametro <code>refresh-check-delay</code> serve a Spring per verificare se il file è stato aggiornato. il valore 5000, espresso in millisecondi, significa "verifica ogni 5 secondi se il file è stato aggiornato". script-source invece è il path (non il package, per questo ci vogliono gli slash).</p>
<p>Per concludere un bel test JUnit, quindi da Java chiamiamo il bean Groovy (che chiama un altro bean Java) :</p>
<pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@ContextConfiguration</span><span class="token punctuation">(</span>locations <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">"file:context/application-context-junit.xml"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token annotation punctuation">@TransactionConfiguration</span><span class="token punctuation">(</span>defaultRollback <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">)</span><br /><span class="token annotation punctuation">@Transactional</span><br /><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">GroobyBeanTest</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractTransactionalJUnit4SpringContextTests</span> <span class="token punctuation">{</span><br /><br /> <span class="token annotation punctuation">@Autowired</span><br /> <span class="token keyword">private</span> <span class="token class-name">GroovyScripterInterface</span> groovyScripterInterface<span class="token punctuation">;</span><br /><br /> <span class="token annotation punctuation">@Test</span><br /> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">groovyTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> groovyScripterInterface<span class="token punctuation">.</span><span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /><span class="token punctuation">}</span></code></pre>
<h2>Conclusioni</h2>
<p>sembra di aver imboccato la strada giusta. Agli "specialisti" del prodotto darò in dote un fantastico linguaggio di scripting con cui potranno fare quello che vogliono. Nel frattempo vi chiedo: avrei potuto seguire altre strade, conoscete altri strumenti per questo compito?</p>
Creare dinamicamente Groovy beans in Spring
2016-01-26T10:10:01Z
https://michelenasti.com/2016/01/creare-dinamicamente-groovy-beans-in-spring/
<p>Il problema che voglio provare a risolvere è questo: data una stringa in ingresso, che contiene il nome di uno script Groovy da eseguire, siamo capaci di trovarlo su db, di istanziarlo e lanciarlo?</p>
<p>Sembra banale alla luce di quanto già scritto nell'<a href="http://michelenasti.com/2016/01/realizzare-un-piccolo-motore-di-scripting-in-una-webapp-java-con-groovy/">articolo precedente</a>, ma voglio aggiungere un ulteriore tacca di complessità: lo script è una classe Groovy che contiene annotations di Spring. Se venisse inizializzato da zero, molto probabilmente le variabili istanza iniettate da spring sarebbero null.</p>
<p>Grazie a queste pochissime righe possiamo superare il problema e goderci i nostri bean _spring_ati:</p>
<pre class="language-groovy"><code class="language-groovy"><span class="token keyword">package</span> interoperability<span class="token punctuation">.</span>groovy<br /><br /><span class="token keyword">import</span> interoperability<span class="token punctuation">.</span>GroovyScripterInterface<br /><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Autowired<br /><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>context<span class="token punctuation">.</span>ApplicationContext<br /><span class="token keyword">import</span> org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>transaction<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span>Transactional<br /><br /><span class="token keyword">class</span> <span class="token class-name">GroovyBeanService</span> <span class="token keyword">implements</span> <span class="token class-name">IGroovyBeanService</span> <span class="token punctuation">{</span><br /><br /> <span class="token annotation punctuation">@Autowired</span><br /> ApplicationContext applicationContext<br /><br /> <span class="token annotation punctuation">@Transactional</span><br /> <span class="token keyword">def</span> <span class="token function">initializeBean</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><br /> <span class="token comment">//leggi la classe da DB, file, etc.</span><br /> <span class="token keyword">def</span> classString <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">File</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">"path/to/GroovyScript.groovy"</span></span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">"\n"</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//uso la console per istanziarlo davvero</span><br /> <span class="token keyword">def</span> gcl <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GroovyClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token keyword">def</span> clazz <span class="token operator">=</span> gcl<span class="token punctuation">.</span><span class="token function">parseClass</span><span class="token punctuation">(</span>classString<span class="token punctuation">)</span><br /><br /> <span class="token comment">//now that the file is compiled, can we istantiate it?</span><br /> <span class="token keyword">def</span> instance <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">newInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//autowire tutti i bean di Spring</span><br /> applicationContext<span class="token punctuation">.</span><span class="token function">getAutowireCapableBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">autowireBean</span><span class="token punctuation">(</span>instance<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <br /> <span class="token comment">//proviamo a chiamarlo!</span><br /> instance<span class="token punctuation">.</span><span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <br /> <span class="token keyword">return</span> instance<br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Il bean <code>GroovyBeanService</code> viene esplicitamente inizializzato da Spring, motivo per cui implementa l'interfaccia <code>IGroovyBeanService</code> .</p>
<p>A riga 21 leggo il contenuto dello script e lo trasformo in una grande stringa.</p>
<p>Attraverso il <code>GroovyClassLoader</code> (riga 24-25) possiamo istanziare la classe appena caricata e poi istanziarla a riga 28.</p>
<p>L'oggetto però ottenuto fino a questo passo conterrà variabili istanza null; tramite l'applicationContext riusciamo a colmare anche questa lacuna. Dopodichè si tratta solo di chiamare i suoi metodi e utilizzarlo.</p>
<h3>Attenti ai MemoryLeak</h3>
<p><code>GroovyClassLoader</code> può prendere in input sia una <code>String</code> sia un <code>File</code> . Quando prende una stringa, circostanza a cui noi per esigenze strutturali non possiamo rinunciare, GroovyClassLoader non riesce a _cache_are la classe e gli oggetti appena creati non saranno cancellati dalla memoria. Tutto questo non accade se invece l'input è un file. Occorre quindi implementare un meccanismo di cache artigianale (ve lo dovete fare voi!) per evitare di reistanziare milioni di volte lo stesso bean.</p>
<p>Buon Groovy!</p>
Guida agli annunci di lavoro per sviluppatori
2016-01-26T18:22:27Z
https://michelenasti.com/2016/01/guida-agli-annunci-di-lavoro-per-sviluppatori/
<p><img src="https://michelenasti.com/uploads/2016/01/wp-1453828848876.jpg" alt="Guida agli annunci di lavoro per sviluppatori" /></p>
<p>Thanks <a href="http://esposi.to/">Esposi.to</a>.</p>
Why Groovy is not that famous?
2016-01-28T13:30:16Z
https://michelenasti.com/2016/01/why-groovy-is-not-that-famous/
<p>If you understand Italian, my last two articles were about integrating Groovy inside a Java webapp.</p>
<h4>But... what is Groovy?</h4>
<ul>
<li>
<p><strong>Groovy</strong> is a <strong>dynamic scripting language</strong>, it can be interpreted at runtime to create applications more rapidly. However, <strong>you can also compile the code</strong> to get the best possible performance (comparable to Java).</p>
</li>
<li>
<p>It has a <strong>succinct syntax</strong> with idioms coming from Javascript and Python, other than Java. There are idioms to avoid null pointers that are very smart and easy to apply.</p>
</li>
<li>
<p>It is <strong>compatible with Java6 syntax,</strong> in the sense that if you write Java6 code it will be 99% understood by Groovy. It would be stupid to write Java inside Groovy, but this is the main advantage Java developers find in this language: they don't have to learn something totally new.</p>
</li>
<li>
<p>It is <strong>one of the</strong> <strong>oldest alternative languages for the JVM,</strong> this year it should have passed 10 market years.</p>
</li>
<li>
<p>It has <strong>closures</strong> and they work in a very natural way; functions are prime-order objects of this language so you can assign and operate over functions pretty much like you would do with int, double, String.</p>
</li>
<li>
<p>It will <strong>wrap every primitive value</strong> by it's Object representative and it will override ‘==' so it will behave like <em>equals</em>. Many other helpers (like type conversion) are present too.</p>
</li>
<li>
<p>It has <strong>type inference</strong>: you can avoid writing the return type of functions and Groovy will try to infer it for you. You can <strong>def</strong> every variable, or you can explicitly type <strong>int, double, String</strong> to gain more compiler power.</p>
</li>
<li>
<p>It has a <strong>good IDE Support:</strong> IntelliJ will support it out of the box, Eclipse is a pain whatever you want to try outside Java but you can accomplish some results.</p>
<p><strong>Annotations,</strong> and <strong>popular frameworks</strong> (like Spring, or Hibernate) <strong>will work seamlessly.</strong> Just put the Jars in the classpath.</p>
</li>
</ul>
<h4>Disdvantages</h4>
<ul>
<li><strong>During the first years of its life Groovy's performances were poor</strong>. Very poor. This was mainly due to a feature of the JVM that was blocking dynamic languages. From Java7 a new feature called <em>invokeDynamic</em> greatly improves dynamic code execution; the compiled groovy classes usually share the same execution time of Java (kind of). However, Groovy is still seen as the "slow" JVM language. Let's change opinion!</li>
<li><strong>People coming from iterative Java will not understand functional paradigms</strong> and will say that Groovy is too difficoult or crazy.</li>
<li>Many are asking, now that <strong>Java8</strong> is here and <strong>supports lambda expressions</strong> (aka <em>functional paradigm</em>), do we still need Groovy? Well, if you also need a dynamic <em>scripting</em> language the answer is yes.</li>
</ul>
<h3>Bonuses</h3>
<p>Ruby on Rails started the framework hype, so now we have Laravel for Php, Play for Scala and Java, Django for Python... <strong>Grails</strong> is the Groovy version of <strong>web development framework</strong>. I admit that i still have no experience with it but I'm very curious.</p>
<p><strong>Gradle is a dependency management and build system</strong> (maven competitor) that is gaining a lot of notoriety since it is the default Android dependency management. It is written in Groovy and it's build file is in groovy syntax, but nobody will notice it if you don't tell them.</p>
<h3>Conclusions</h3>
<p>Since I see a lot of good points and a very limited list of disadvantages, why Groovy has not shined like a star? Like... Scala, for example. It is seen as a simple tool, like the very first versions of javascript, but I have done many complex things that cross the boundaries of one programming language. And now I will develop a Grails app to see its power.</p>
Sarò al Codemotion 2016, Roma
2016-01-29T18:00:39Z
https://michelenasti.com/2016/01/saro-al-codemotion-2016-roma/
<p>Anche quest'anno parteciperò al <a href="http://rome2016.codemotionworld.com/">Codemotion 2016</a> di Roma, che per distanza geografica è anche la più facile da raggiungere. Quest'anno una tiratina di orecchie va proprio allo staff, che ha messo il biglietto "starter" al doppio dell'anno scorso. Maledetti!</p>
<p>Nonostante questo ho deciso di partecipare anche a un <a href="http://rome2016.codemotionworld.com/workshop/developing-rest-apis-with-node-js-and-hapi/">workshop</a>, ossia un evento "hands-on" in cui mi sporcherò le mani con node.js e un framework, HAPI, di cui non sospettavo nemmeno l'esistenza.</p>
<p>Sarò sincero, il workshop lo vorrei usare anche per <strong>spronarmi a studiare più node.js</strong>. Inoltre poter parlare con altre persone che sviluppano costantemente in Node mi permetterà di vedere il loro modus operandi, ossia che tool usano, che struttura per le directory e per i file, che IDE, che filosofia, etc.. <em>Dedicare 8 ore delle mie ferie a questo mi fa sentire un cretino</em>, ma poi penso che lo sto facendo per il mio bene.</p>
<p>In ogni caso, per 3 giorni sarò a Roma quindi se volete ci vediamo lì e ci prendiamo una birra. Se mi siete simpatici potrei addirittura offrirvela io!</p>
I colloqui di lavoro iniziano (e finiscono) su facebook
2016-01-31T23:02:35Z
https://michelenasti.com/2016/01/i-colloqui-di-lavoro-iniziano-e-finiscono-su-facebook/
<p><em>Storia reale, non faccio nomi, ma se ti senti ipoteticamente coinvolto potrebbe essere che stia parlando di TE.</em></p>
<p>Ho un'amica che gestisce un piccolo ufficio e quest'anno aveva bisogno di ampliare l'organico, e ha iniziato la sua campagna di <em>recruitment</em> facendo girare il passaparola.</p>
<p>Ecco che la mia amica ha fatto una cosa che nessuno dice ma che tutti fanno, ossia ha <strong>aperto facebook e inserito nomi e cognomi</strong> per farsi un'idea dei potenziali candidati.</p>
<p>Il caso più eclatante è stato un ragazzo, che ha come status "defunto presso <em>obitorio"</em>, "ha studiato presso <em>analfabeta (precedente: university of Secondigliano)"</em>, e tra le foto-profilo ci sono anche alcune oscenità. Non so come sia andata a finire, non penso bene.</p>
<p>Qualcuno potrebbe ribattere che qualsiasi pagina Facebook, specialmente quelle dei ventenni, sia così. Che <em>un selezionatore non dovrebbe fermarsi all'apparenza</em>, che avrebbe dovuto concedere una chance al ragazzo. Magari era capace. Voi che avreste fatto?</p>
<p>Questo è un caso eclatante ma ormai normale di una fase di recruiting. Spesso io vengo contattato e il mio blog (oltre che i miei account social) parlano per me... Nel caso di questo ragazzo invece bisognerebbe ricordarsi chi ha il coltello dalla parte del manico (il recruiter).</p>
<p>Ricordatevi: agli occhi di chi non vi conosce, quel che siete su internet è la sua prima impressione. Vedrà i vostri account, facebook twitter instagram, e si farà un'idea. E' per questo che ho lasciato "morire" il mio vecchio blog in favore di quest'altro... lì c'erano troppe cavolate universitarie, post politici, opinioni religiose, che adesso non mi rappresentavano più. Non voglio che sia quella la prima impressione che si deve avere di me.</p>
<p>Come scrivevo prima, questo atteggiamento mi ha portato anche qualche vantaggio, seppure non immediato. Ogni tanto riesco a entrare in contatto con persone decisamente interessanti, seppure mai conosciute realmente, che hanno interessi e conoscenze simili alle mie. E anchea a proposte di opportunità lavorative. Questo potrebbe succedere anche a voi, se siete disposti a investire sul <strong>personal branding</strong>, ma <em>prima che sia troppo tardi</em> dovreste lavorare sul vostro profilo e cancellare le foto imbarazzanti, tipo quelle di quando avevate 13 anni (...quelle con la scritta viola "quanto sono bella da 1 a 10?").</p>
Alla scoperta del Machine Learning
2016-02-08T09:40:09Z
https://michelenasti.com/2016/02/alla-scoperta-del-machine-learning/
<p>Ancora una volta non ho saputo resistere alla tentazione e ho iniziato lo studio di una tematica molto complessa: il <strong>Machine learning</strong>.</p>
<p>In realtà già ai tempi dell'Università abbiamo studiato qualcosa a riguardo, sebbene dal punto di vista estremamente statistico (e con <em>R</em>). La cosa incredibile è che questi sistemi, che 5 anni fa pensavo troppo futuristici, sono ovunque e da un bel po'. Pensate a Amazon quando vi propone un oggetto simile, a Google quando vi suggerisce una correzione ("forse cercavi..."), a LinkedIn che vi propone persone simili a voi.</p>
<p>Senza entrare nello specifico, attraverso il machine learning possiamo simulare una serie di comportamenti del nostro cervello:</p>
<ul>
<li>prevedere un valore data una serie di valori simili (es. Il valore di una casa in base al valore di altre case)</li>
<li>analisi dei sentimenti (recensioni positive o negative)</li>
<li>deep learning (interpretare fotografie/oggetti complessi e il loro contenuto)</li>
<li>Sistemi di raccomandazione automatici (se ti piace questo, potrebbe piacerti anche...)</li>
<li>natural language processing</li>
</ul>
<p>Ma non bisogna essere grandi nomi dell'IT per avere queste funzionalità. Né c'è bisogno di capire matematica avanzata o concetti di programmazione astrusi. Il Machine learning sta diventando accessibile a tutti grazie a una serie di librerie e con un po' di buona volontà. L'analisi dei Big Data sta diventando cruciale!</p>
<p>Resta da spiegare come sto affrontando lo studio di questi argomenti: da un punto di vista essenzialmente pratico, con <a href="https://www.coursera.org/learn/ml-foundations/home/welcome">Coursera</a>, e tramite Python (sempre più il linguaggio di riferimento per il data science) e una libreria chiamata GraphLab. Esistono tutta una serie di funzioni già scritte che, preso un input, sono capaci di simulare i modelli statistici precedentemente descritti. Il corso in realtà dopo uno studio pratico diventa teorico, con la spiegazione degli algoritmi utilizzati e come funzionano.</p>
<p>Se volete studiarlo insieme a me, è il momento giusto. Ho appena completato la prima lezione e sto iniziando la seconda. Ci sono purtroppo delle scadenze per consegnare gli esercizi quindi... Partiamo!</p>
Quando possiamo dire che conosciamo una tecnologia?
2016-02-15T11:06:14Z
https://michelenasti.com/2016/02/possiamo-dire-conosciamo-tecnologia/
<p>Inizio con un finto annuncio di lavoro. In questi giorni è rimbalzato ovunque su internet quindi è probabile che l'abbiate già letto.</p>
<h3>Come sarebbe se gli autisti fossero assunti come i programmatori? (originale <a href="https://www.jitbit.com/alexblog/203-what-if-drivers-were-hired-like-programmers/">qui</a>).</h3>
<blockquote>
<p><strong>Cosa cerchiamo:</strong> autista</p>
<p><strong>Requisiti:</strong> capacità professionali di guida di automobili normali e pesanti, autobus e camion, autobus con rimorchio, tram, metro, trattori, pale scavatrici, carrarmati contemporanei leggeri e pesanti in uso presso le nazioni NATO.</p>
<p>Obbligatorio: capacità di guida spericolata e rally.</p>
<p>Bonus: esperienza di guida in Formula 1.</p>
<p>Obbligo di conoscenza ed esperienza nella riparazione di pistoni e motori a rotori/wankel, trasmissioni automatiche e manuali, sistemi di iniezione, computer di bordo, ABS, ABD, GPS e autoradio delle aziende più conosciute.</p>
<p>É gradita esperienza in verniciatura auto e lavori si carrozzeria.</p>
<p>Gli aspiranti autisti devono avere certificati di BMW, General Motors e Bosch, non più vecchi di 2 anni.</p>
<p><strong>Paga</strong>: €15-€20/ora, a seconda delle reali competenze dell'intervistato.</p>
<p>**Titoli di studio richiesti:**Laurea quinquiennale in Ingegneria.</p>
</blockquote>
<h2>No, ma dai, Davvero?</h2>
<p>Si cari amici, più o meno per noi informatici è così 🙂 l'università per fortuna ci forma ad apprendere, e ci dà una serie di strumenti base - mattocini lego - su cui tutte le altre tecnologie sono state costruite. Questo non vuol dire che possiamo imparare tutto e soprattutto in poco tempo: i framework, i linguaggi di programmazione e gli esperimenti hanno bisogno che gli si dedichi tempo per poter poter essere digeriti.</p>
<h4>E allora quanto ci vuole ad imparare un linguaggio?</h4>
<p>A me piace invece pensare che <strong>conosci bene un linguaggio</strong>, o un framework o una nuova tecnologia, <strong>quando ne sai risolvere i problemi</strong>.</p>
Ho partecipato a Google HashCode 2016. Ecco com'é andata
2016-02-15T20:23:33Z
https://michelenasti.com/2016/02/ho-partecipato-a-google-hashcode2016-ecco-come-andata/
<p>Qualche sera fa io e altri amici abbiamo deciso di partecipare al <strong>Google HashCode 2016</strong>, una gara di programmazione distribuita ideata da Google per team da 2 a 4 persone. Il mio non era proprio un team di <em>scemi</em>, siamo tutti usciti dall'università da qualche anno (...) ma avevamo ancora voglia di rockeggiare. Avevamo <strong>4 ore di tempo</strong> per presentare una soluzione a un problema comune e potevamo usare <strong>qualsiasi linguaggio</strong> di programmazione avessimo bisogno.</p>
<h2>il problema</h2>
<p>Quest'anno si é parlato tanto di <strong>droni</strong> per consegnare pacchi: da Amazon a AliBaba tutti hanno avviato delle sperimentazioni e sono usciti molti articoli di giornale che annunciavano questo futuro non troppo lontano.</p>
<p>Sebbene Google non sia proprio un'azienda di shopping online, lo statement di oggi era proprio questo: <em>data una lista di M depositi, una lista di D droni e una lista di N ordini da evadere che potevano contenere più oggetti non tutti presenti nello stesso deposito, saremmo riusciti a pianificare il volo dei Droni in modo da evadere più ordini possibile?</em></p>
<p>Al problema mancano altre due vincoli, ossia i droni possono portare un peso massimo e la durata della simulazione é prefissata. Quanti ordini saremmo riusciti a evadere prima della scadenza del tempo?</p>
<h2>Bullshit! I "veri" programmatori non risolvono questo tipo di problemi</h2>
<p>Ah si? E chi li risolve allora? Questi sono i problemi che competono ai programmatori! I siti web e i gestionali lasciamoli fare a quelli scarsi 😀</p>
<h2>Input e output</h2>
<p>Per rimanere il più agnostici possibile dal linguaggio di programmazione, gli input di Google erano file di testo con righe di numeri. Anche l'output doveva essere codificato in un certo modo (sempre numeri in un file di testo semplice) che poi un giudice automatico avrebbe validato. Solo per fare il parsing dei primi file abbiamo bruciato un'ora su 4 a disposizione.</p>
<h2>Il nostro algoritmo</h2>
<p>All'inizio volevamo arrivare il più presto possibile nel judge system quindi abbiamo provato a scrivere l'algoritmo più semplice che ci venisse in mente: ogni drone porta max 1 oggetto (dunque ordini grandi avrebbero richiesto più viaggi).</p>
<p>Questo algoritmo si é preso 3 ore per rispettare tutti i constraint e, comunque, <em>non funzionava a dovere.</em></p>
<h2>Idee per l'anno prossimo</h2>
<p>Una delle cose che abbiamo fatto bene è stata quella di scrivere al più presto un modello dati comune a tutt'e quattro; un'altra cosa che mi è piaciuta è stata l'idea di scrivere delle classi utility che permettessero di ragionare sulle collezioni di dati (una sorta di DB, diciamo).</p>
<p>Ma tante altre cose non le abbiamo fatte bene. Abbiamo scelto un linguaggio che 2 del nostro team non ricordavano più, e quindi non potevano dare una mano (in realtà hanno aiutato eccome, hanno scritto lo pseudocodice dell'algoritmo...). Inoltre siamo arrivati al test negli ultimi 10 minuti a disposizione, quando era chiaro che non c'era più niente da fare: infatti non ha funzionato. Quindi una delle cose che dovremmo provare a fare è di inviare subito qualcosa al Judge System per avere una validazione dei dati più rapida. E dobbiamo scrivere più in fretta l'algoritmo altrimenti rischiamo di non poterlo ottimizzare.</p>
<h2>Conclusioni</h2>
<p>E' stata una prova molto difficile, un problema sicuramente non banale. Ma partecipare nell'Hub dell'<strong>Università di Salerno</strong> ha conferito quell'atmosfera di <em>nerdismo</em> che ci ha fatto sentire, ancora una volta, <em>studenti</em>.</p>
<p>Ci riproveremo? Certo. Se pure dovesse andare male, ci consoleremo con i gadget.</p>
<p><img src="https://michelenasti.com/uploads/2016/02/2016-02-11-18.31.36.jpg" alt="gadget" /></p>
"quanto ci metti?" - perchè i programmatori sono delle pippe col time management
2016-02-19T12:47:00Z
https://michelenasti.com/2016/02/quanto-ci-metti-problemi-time-management/
<p><img src="https://i2.wp.com/michelenasti.com/uploads/2016/02/time-management.jpg" alt="" /></p>
<p>Il boss vi spiega una funzionalità da implementare, una bella cosa che va dal front-end al db, regole di business, interazioni UI, chiamate a servizi esterni, etc. e poi la fatidica domanda: "quanto ci vuole?" e voi, candidamente e onestamente, rispondete gentili: "mah, un giorno credo..."</p>
<p>Questo problema affligge tutti, e gli unici che non sbagliano sono quelli che sovrastimano. (Del resto, ai capi piace quando dici 3 e ci metti 2, mentre non piace se dici 2 e ci metti 3.)</p>
<p>Ma a cosa è dovuto questo <em>annoso</em> problema?</p>
<ul>
<li>Quando facciamo delle stime assumiamo il caso migliore, ossia nessuna interruzione, i requisiti non cambiano, etc.</li>
<li>sottostimiamo la complessità delle cose da fare</li>
<li>sovrastimiamo le nostre abilità</li>
<li>Per il boss, "stima" vuol dire "previsione esatta al 100%"</li>
</ul>
<p>E gli effetti ricadono sia su di voi (generalmente con una bella aggiunta di stress), sia sul lavoro che dovevate fare (fatto in fretta, magari dovevano partire altre attività che restano bloccate, etc).</p>
<h3>Possiamo stimare meglio?</h3>
<p>La risposta onesta è: <strong>no</strong>. Questo è avvilente, soprattutto perchè in genere i programmatori sono persone intelligentissime che hanno studiato una miriade di cose complesse (teoremi, linguaggi, etc), e pensano di essere supereroi che superano le leggi fisiche. Ma sulla stima non c'è niente da fare, <strong>il tempo è l'unica risorsa consumabile che non tornerà mai più indietro</strong>. Non lo dico solo io: da un lato Zakas ha scritto un <a href="https://michelenasti.com/archives/saved_elements/time_management_zakas.html">messaggio in una mailing list</a> a riguardo, ma in rete ci sono molti altri articoli, <a href="http://www.drdobbs.com/architecture-and-design/software-estimation-how-misperceptions-m/240166474?utm_source=NCZOnline+Newsletter&utm_campaign=5afe08eb6f-ncznewsletter&utm_medium=email&utm_term=0_607705c87a-5afe08eb6f-87891737">tipo questo</a>.</p>
<p>Zakas suggerisce di <strong>arrotondare</strong> sempre <strong>al giorno intero</strong> se il task dura meno di una settimana (es. stimo 1,5 giorni => dico 2 giorni), di <strong>arrotondare alla settimana</strong> se dura più di 5 giorni (Es. stimo 9 giorni => dico 2 settimane) (ricordate che in una settimana lavorativa ci sono 5 giorni :p ), e se stimate di più beh,,, scomponete il problema, non potrete mai stimare una cosa che dura mesi!.</p>
<h2>Altri metodi di Time Management</h2>
<h3>Pomodoro Techique</h3>
<p>Quasi tutti ora direte "ma c'è la Pomodoro Technique!", ossia dividere il tempo in slot da 25 minuti e fare una pausa di 5 minuti tra ogni slot; ogni 4 slot consumati fare una pausa più lunga (15 minuti). La tecnica Pomodoro è un modo per provare a lavorare concentrati, non un modo per stimare la durata dei task.</p>
<p>Questa tecnica è ampiamente utilizzata e pubblicizzata nel mondo, esistono tantissimi siti web, libri e app (eccone <a href="http://tomato-timer.com/">uno</a>) per provare a usarla.</p>
<p>Purtroppo, almeno nel mio caso, la tecnica Pomodoro non funziona affatto perchè le interferenze esterne sono la normalità ovunque abbia lavorato, per non parlare di quando provo a fare qualcosa a casa. (Butta l'immondizia... prendi le cose in macchina... si lavora sempre per qualcuno!). Inoltre le poche volte che ho provato a usare strumenti software per monitorare il tempo ho finito col litigarci - tipo quando compare quel popup "fai una pausa!" e io sto nel bel mezzo di un algoritmo complicatissimo.</p>
<h3>Scrum</h3>
<p><strong>Scrum</strong> è una metodologia <em>Agile</em> che ho conosciuto nella prima fase della mia vita lavorativa. E' un insieme di best-practices per organizzare il lavoro, basato sull'esperienza ventennale di centinaia di migliaia di sviluppatori nel mondo. Questa delle metodologie software è un campo in continuo fermento.</p>
<p>Scrum organizza gli <strong>sprint</strong> in cicli da 2 settimane, ossia ogni 2 settimane si consegna un <strong>deliverable</strong> pienamente testato e funzionante con le nuove feature aggiunte. Queste feature, che in scrum si chiamano <strong>user stories</strong>, non vengono stimate in base al tempo che ci vuole per farle, ma in base a una metrica chiamata "<strong>story point</strong>", che il team assegna a ogni storia. Questi punti non sono un'indicazione di quanto tempo ci vuole ma di quanto il team vede complessità nei prossimi task da svolgere.</p>
<p>Come si relazionano questi punti con il tempo? In Scrum c'è tutta una filosofia per cui il team sa che ogni sprint riesce a bruciare X story points, anche basandosi sulle metriche raccolte nei precedenti sprint, quindi il tempo non diventa più un grande problema (perchè è fisso) bensì le storie, se sono percepite come molto complesse dal team, possono diventare lunghe e spalmarsi su più sprint. In questi casi la soluzione migliore è di spacchettare le storie in sottostorie più semplici (e misurabili).</p>
<p><em>Studi empirici dimostrano che gli story point non sono proporzionali al tempo ma al costo (dollaroni). Se un task è difficile (=punteggio alto) probabilmente costa anche di più realizzarlo. Se un task è facile, invece, non è detto che sia di breve durata.</em></p>
<h3>Per concludere</h3>
<p>Avete migliorato il vostro modo di gestire il tempo leggendo questo articolo? Suppongo di no. Ma almeno ora sapete che non siete i soli ad avere questo "problema". Se conoscete altri modi di stimare / gestire il tempo, segnalatelo nei commenti.</p>
Il battesimo del sangue: il talk su NodeJS al Napoli DevDay
2016-02-25T19:00:36Z
https://michelenasti.com/2016/02/il-battesimo-del-sangue-il-talk-su-nodejs-al-napoli-devday/
<p>Ieri sera (24/02/2016) ho presentato NodeJs alla platea del Napoli DevDay.</p>
<p>Le slide, con le gif animate e tutto, le potete <a href="https://docs.google.com/presentation/d/1EDsvR99WUDx3IBhygYt51YKM1sA5UA_PJmaRFh9HTa4/edit?usp=sharing">vedere su google docs</a>.</p>
<p>Il repo con il codice mostrato al talk è su <a href="https://github.com/musikele/nodeJsExamples">github</a>, insieme a una descrizione dei vari file.</p>
<p>Se avete osservazioni, dubbi, chiarimenti, consigli, giudizi, sono a vostra disposizione.</p>
<p>Ho intitolato questo post "<em>il battesimo del sangue</em>" perchè, sul più bello, non mi ha funzionato un esempio. Nessun dramma, era una fesseria: il codice è 100% funzionante, ma l'estensione di Chrome che uso per testare le API REST, chiamata Advanced Rest Client, codificava le richieste in un altro modo (non-JSON) e il server Node non la interpretava correttamente. Settando l'encoding a <code>application/json</code> funziona tutto.</p>
<p>E non dimenticate del <a href="http://www.meetup.com/it-IT/JS-Salerno/">Meetup su Javascript</a> il 5 marzo 2016! Maggiori dettagli in un prossimo post.</p>
Considerazioni sul Public Speaking
2016-02-27T10:10:23Z
https://michelenasti.com/2016/02/considerazioni-sul-public-speaking/
<p>Qualche giorno fa ho tenuto il talk su NodeJS al Napoli DevDay, 30-40 minuti di pura pazzia. Nel 2015 ho parlato, sempre di NodeJs, al Linux Day a Salerno e anche lì mi sono dovuto misurare con una bella platea. Dunque, con due talk all'attivo, posso discutere di public speaking? Essendo questo il mio blog, direi proprio di SI 🙂 Quindi eccovi alcune considerazioni fresche di giornata.</p>
<h3>Slide come canovaccio</h3>
<p>Non sono bravissimo a fare le slide, per fortuna compenso con il chiacchiericcio. Però fare delle buone slide è fondamentale. <a href="http://michelenasti.com/2015/05/basta-ai-powerpoint-fatti-male/">Qui un articolo con suggerimenti su come prepararle</a>. Le slide non devono contenere troppo testo (la gente è lì per voi!) ma solo le parole chiave e immagini esplicative. Va bene anche qualcosa per mantere viva l'attenzione (GIF? Chi ha detto GIF?!)</p>
<h3>Fai una Demo del talk</h3>
<p>Allo specchio, o con qualcuno che possa ascoltarti, anche se non può capire tutto. Quando dovevo preparare la tesi di laurea i miei genitori hanno dovuto ascoltare cose che non hanno capito, ma l'hanno fatto di buon grado. Ora mia moglie è una delle mie cavie preferite.</p>
<h3>Cerca di usare il tuo PC (compra l'adattatore vga/hdmi/etc)</h3>
<p>Sul tuo computer hai sicuramente risolto tutti i problemi che potrebbero capitare sui computer degli altri: monitor che si spengono, aggiornamenti adobe, antivirus, etc. E poi conosci tutti i tasti magici e i tool che ti servono.</p>
<h3>Gif! Gif!</h3>
<p>Le gif sono simpaticissime per mantenere viva l'attenzione. Io ne faccio un abuso, come tutto il web del resto. Trovate ottime Gif su Giphy o <a href="http://thecodinglove.com/">the coding love</a>. Ecco, ora non leggerete più il resto dell'articolo.</p>
<h3>Evitare il codice</h3>
<p>Come si fa a parlare di programmazione senza far vedere codice?!??! Lo so, me lo chiedevo anche io, ma spesso ci sono concetti da spiegare che valgono molto di più del codice che mostrerete. Diceva ieri un mio collega, quando si spiega col codice sembra di essere a una lezione di Laboratorio di Algoritmi, dove se ti distrai un attimo non capirai più niente. Un talk non dovrebbe essere così.</p>
<h3>Ma se c'è qualcosa che si capisce col codice, allora si</h3>
<p>Contemporaneamente lo stesso collega mi ha detto che alcuni snippet sono stati utilissimi per capire la filosofia, quindi la mia non è una chiusura totale ma un invito a usare il codice i maniera ragionata.</p>
<h3>Non è una gara a chi ne sa di più</h3>
<p>Io non ho sviluppato la piattaforma NodeJs, anzi sono un semplice appassionato che per lavoro fa altro, quindi con NodeJS ho addirittura poca esperienza sul campo. Eppure preparare un talk ti fa scendere in molti dettagli obbligandoti a capire cose che non avresti mai pensato di dover capire. E che poi bisogna spiegare.</p>
<p>La mia paura è sempre stata quella di trovare persone che a fine talk vengono e mi dicono: "cosa ne pensi di X? e di Y?" e io, a malincuore, sono costretto a dire che non ne so nulla di questi argomenti. Mi è capitato ieri, a fine talk un ragazzo mi ha chiesto di Electron (framework per creare native apps in nodejs) e io, francamente, non ne so nulla. Anzi lui lo aveva provato e ne sapeva più di me. Complimenti! Stringetegli la mano e aggiungetelo su Facebook.</p>
<h3>Chiedete Feedback onesti dopo il talk</h3>
<p>Quasi tutti quelli che non vi conoscono vi diranno che siete andati bene, mentre gli amici si faranno meno problemi a dirvi cosa c'è che non va. Però dovete chiederlo altrimenti non ve lo diranno. Io penso che sia fondamentale sapere quali parti della presentazione siano arrivate al pubblico e quali no. Alcuni punti di questo articolo sono venuti fuori proprio parlando con loro.</p>
<p>Mi raccomando, accettate le loro critiche, ma non commettete l'errore di pensare di aver sbagliato tutto. Le sensazioni personali valgono molto di più, bisognerebbe quasi estraniarsi e rivedersi per capire che cosa si è fatto bene e cosa si è fatto peggio. Come dice un amico: <a href="http://www.raffaelegaito.com/feedback-startup-cosa-ho-imparato/">senti tutti, ascolta pochi, segui nessuno</a>.</p>
<h3>Public Speaking non è per tutti</h3>
<p>Ho visto talk veramente imbarazzanti e noiosissimi, e altri invece fantastici e divertenti. Il segreto? Lo speaker. Tuttavia se non ce l'avete nel sangue non posso dirvi di lasciare perdere. <strong>Dovete comunque provarci</strong>. Scoprirete quanto è difficile essere "dall'altra parte" e, soprattutto, ne guadagnerete in visibilità. I meetup o le conferenze poco affollate sono perfette per iniziare.</p>
<h3>La visibilità</h3>
<p>Il primo step atteso è di essere riconosciuti come "conoscitori" dell'argomento presentato. Conosco un tizio che ha scritto un libro su Laravel, anzi ha scritto un libro su come lui usa Laravel; egli non si considera un esperto eppure il mondo lo incensa e lo celebra. Nel giro di un paio d'anni dovreste iniziare a vedere anche qualche beneficio "economico": aziende vi contattano per lavorare con loro come guru/esperto. Insomma, conoscere bene una cosa non basta, bisogna dimostrare di conoscere bene una cosa e le conferenze sono un ottimo modo per farsi notare.</p>
<h3>"C'erano solo X persone"</h3>
<p>Non importa quante persone vi ascolteranno. Per ora ho tenuto due talk, uno davanti a 15 persone e un altro davanti a 50. In entrambi i casi vedrete i seguenti personaggi: quello che si fa i fatti suoi, quello che dorme, quello che gioca col telefonino, quello che vi ascolta attentamente. Ignorate i primi e parlate per questi ultimi, sono loro quelli veramente interessati.</p>
<p>Infine, anche se vi ascolteranno fisicamente in pochi, pubblicizzate le slide e l'evento sul vostro sito web. Parlare per 15 o per 150 richiede lo stesso impegno per preparare slide, metafore ed esempi, quindi non bisogna cadere nell'errore di non pubblicizzarsi. Slideshare, il vostro blog (non ce l'avete!?!?!?), Linkedin e Facebook sono ottimi luoghi dove diffondere il messaggio. <a href="http://michelenasti.com/2016/01/i-colloqui-di-lavoro-iniziano-e-finiscono-su-facebook/">I recruiter apprezzeranno...</a></p>
Javascript Meetup a Salerno!
2016-02-29T08:15:19Z
https://michelenasti.com/2016/02/javascript-meetup-a-salerno/
<p>Sabato 5 marzo ci sarà il primo <a href="http://www.meetup.com/it-IT/JS-Salerno/">Meetup Javascript & NodeJS</a> di Salerno!</p>
<p>L'appuntamento è presso la sede della scuola <a href="http://www.puntolingue.it/">Puntolingue</a>, all'interno della stazione FF.SS. di Salerno, sul primo binario, di fianco a Italo. Start ore 16.30 (fa molto discoteca scrivere così...).</p>
<p>Javascript è passato dall'essere un linguaggio di scripting mal-supportato all'essere al centro dell'esperienza utente di qualsiasi webapp (e anche app, ultimamente).</p>
<p>Dopo tante innovazioni e un board che lo ha standardizzato, siamo finalmente giunti alla nuova versione di Javascript chiamata EcmaSscript 6.</p>
<h3>E allora?</h3>
<p>Lavoro quotidianamente con Javascript e sentivo di dover fare qualcosa per diffondere maggiormente questo linguaggio che, nel bene e nel male, è un linguaggio vero e proprio ed è alla base del lavoro di molti di noi.</p>
<p>Sentivo di dover fare qualcosa per la mia provincia, per la mia università e per i colleghi che lavorano a Salerno per questo ho creato il Meetup salernitano.</p>
<p>Sin dal primo momento ho pensato che non avremmo dovuto creare un gruppo solo virtuale come tanti su fb, ma un gruppo di persone che si incontrano fisicamente per scambiare opinioni, per conoscere tecnologie nuove, per fare recruitment e per spingere le persone a fare rete.</p>
<p>Da qualche tempo avevo notato che gli eventi tecnici per sviluppatori, a Salerno o più in generale in Campania, sono molto pochi. Il <a href="http://michelenasti.com/2016/01/saro-al-codemotion-2016-roma/">Codemotion</a> è uno degli eventi nazionali più vicini e importanti, ma è a Roma; in Campania alcuni amici organizzano il <a href="http://napoli.devday.it/">DevDay</a>, a cui ho partecipato come speaker parlando di NodeJS, e abbiamo avuto anche una discreta presenza di pubblico. A Fisciano l'Università organizza spesso eventi ma sempre in orari mattutini, e destinati per lo più agli studenti.</p>
<p>Insomma, non dovete fare altro che partecipare. Sulla pagina del meetup <a href="http://www.meetup.com/it-IT/JS-Salerno/events/228723943/">rispondete con un bel "parteciperò"</a>, ci servirà per organizzare sedie, food & beverage. Invitate chi volete, è gratis. Abbiamo bisogno di voi!</p>
Chi è Jacopo Notarstefano, l'inventore del programma anti-Calderoli
2016-03-04T20:37:24Z
https://michelenasti.com/2016/03/battere-coglioni-usando-un-po-scienza/
<p>In questi giorni, sul web, è uscito un articolo bellissimo: <a href="http://www.jacquerie.it/battere-calderoli-usando-python">Battere Calderoli usando Python</a>, scritto da <a href="https://twitter.com/Jaconotar">Jacopo Notarstefano</a>. E' un po' difficile da leggere perché è tecnico, ma gli effetti sono piuttosto interessanti. Proverò a spiegarvi un attimo l'articolo, da un punto di vista "umano", e poi vi parlerò dell'autore.</p>
<h2>L'articolo originale spiegato facile</h2>
<p>Spero sappiate tutti che Calderoli se ne è uscito, qualche tempo fa, con un programmino informatico (non scritto da lui) capace di generare emendamenti a proposte di leggi. Semplicemente sposta una virgola qui, aggiunge un comma lì, ed ecco che l'emendamento è servito. Con questo programmino ha presentato milioni di emendamenti in occasione del Ddl Boschi che la Camera avrebbe addirittura dovuto stampare; per fortuna in quel caso hanno preso una svolta ecologista e hanno scelto di non farlo. (Perchè stampano se i deputati hanno un tablet "aziendale"?!)</p>
<p>Dunque molti emendamenti sono simili. Come fare a capire quali sono stati generati da software e quali invece sono "reali"? Come eliminare il "rumore" di questi emendamenti fuffa?</p>
<p>Ecco il procedimento utilizzato da Jacopo, spiegato a chi di informatica capisce poco o niente:</p>
<ul>
<li>Il primo passo è di scaricare tutti gli emendamenti dalla pagina del senato.</li>
<li>Il problema da risolvere è un classico problema di Data Science, ossia trovare tutti i "cluster" (gruppi) di articoli "omogenei" (simili).</li>
<li>Come capire quali articoli sono simili tra loro? Usando una metrica semplice: dati due emendamenti, calcola quanto è grande la sottostringa uguale (una porzione di testo) più grande. E' abbastanza chiaro che se la sottostringa è grande, i due articoli sono molto simili.</li>
<li>Grazie a questa metrica è possibile lanciare un algoritmo, chiamato "Hierarchical Clustering", che mostra un grafico somigliante a un tabellone dei mondiali disegnato da un ubriaco. Se lo guardate attentamente, però, i colori sono diversi: bene, l'algoritmo ha colorato allo stesso modo gli emendamenti che secondo lui sono simili. Nell'articolo viene mostrato anche praticamente, ispezionando gli esempi.</li>
</ul>
<h3>Calderoli, prendi questo</h3>
<p>L'approccio qui spiegato è molto simile a quello che fanno giornalmente i filtri anti-spam dei nostri account di posta, per filtrare le mail indesiderate. Spiace dirlo ma un milione di emendamenti di Calderoli (o di chiunque altro) non è che spam, fuffa, e come tale va buttato. Un senatore (pagato da noi) spammer.</p>
<h3>Chi è l'autore?</h3>
<p>Ci sto chattando su Twitter mentre scrivo l'articolo. E' una persona normale, risponde a tutti (compreso me che non sono nessuno), anche se in questi giorni chiunque lo sta contattando per chiedergli un'intervista. Ha 28 anni, è laureato in matematica alla triennale, sta completando la specialistica di informatica; ha iniziato a programmare seriamente dal 2010, e lavora presso il CERN di Ginevra al motore di ricerca di articoli scientifici (ecco spiegata la familiarità con le metriche sui testi e la capacità di analizzare dati di grandi dimensioni...).</p>
<p>Ci tiene a precisare che il suo articolo è stato realizzato lontano dagli orari di lavoro al CERN, dunque nel tempo libero, e che <strong>il CERN non centra assolutamente nulla con questo lavoro</strong>.</p>
<p>Per me questo ragazzo è un genio, Nel senso che ha applicato strumenti che a suo tempo ho studiato anche io ma che non ho mai pensato di usare in questo senso. Gli chiedo se si sente così (<strong>test della modestia</strong>): "Nah. Cioè, non penso di essere scemo, ma ho conosciuto abbastanza gente che si meritava l'appellativo di "genio" per sapere che io non me lo merito." E' anche modesto!</p>
<p>Gli chiedo <strong>se i suoi superiori, o colleghi, gli abbiano chiesto spiegazioni o intimato qualcosa</strong>: "No, nessuno. L'unica cosa che mi ha chiesto il Press Office è di tenere ben presente che questo progetto e il CERN non hanno niente a che vedere l'uno con l'altro." E poi: "il collega italiano si è congratulato dell'articolo, ma niente di più". Immagino che la CERN e specificamente nel suo settore, risolvano problemi ben più complessi. E, dato l'ambiente internazionale, a nessuno frega nulla di Calderoli spara-emendamenti.</p>
<p>Mi conferma che anche <strong>dalle istituzioni politiche non è arrivato nessun segnale di interesse</strong> verso il suo lavoro.</p>
<p>Altra domanda, <strong>quante persone ti hanno contattato in questi giorni per chiederti interviste</strong> e chiarimenti sull'articolo? "Chiunque 🙂 sono un po' indietro con le interviste sebbene stia saltando la pausa pranzo da martedì per farle... Mi sono stati chiesti i dettagli tecnici dell'articolo, ma in genere sono sempre stati tagliati dagli articoli, quindi non li devo aver spiegati granché bene."</p>
<p>A questo punto, curiosità personale: <strong>come si fa a lavorare per il CERN</strong>? Risposta più facile di quel che pensavo: "Applichi a uno dei (tanti) bandi che escono durante l'anno. A seconda del tipo della posizione hai requisiti diversi sulle qualifiche che devi avere e gli step che devi superare. Ad esempio a <a href="https://jobs.web.cern.ch/join-us/fellowship-programme">questo indirizzo</a>.</p>
<p>Non resta che salutarci, è una persona davvero squisita, risponde a tutte le mie domande senza troppi problemi. Gli auguro il meglio, e mi riprometto di far arrivare il suo lavoro più in alto possibile (chi lo sa, magari ai presidenti di Camera e Senato?).</p>
<p>Io ripeto quello che ho già scritto in un tweet: <strong>fatelo</strong> <strong>Presidente Del Mondo</strong>, il mio voto già ce l'ha!</p>
5 motivi per cui Rework ti cambierà la vita
2016-03-09T19:33:14Z
https://michelenasti.com/2016/03/5-motivi-cui-rework-ti-cambiera-la-vita/
<p><a href="http://amzn.to/2nYp7mS"><strong>Rework</strong></a> é un libro.</p>
<p>Gli autori di Rework sono gli stessi autori di <strong><a href="http://www.basecamp.com/">Basecamp</a></strong>, software per il lavoro collaborativo, e di <strong>Ruby on Rails</strong>, tool per lo sviluppo rapido di webapp che va tanto di moda in USA. La loro azienda si chiama(va) 37signals.</p>
<p>All'interno di Rework gli autori descrivono la loro filosofia lavorativa che hanno già applicato a se stessi; insomma non parlano di concetti astratti ma di cose sperimentate e supportate dalla loro esperienza.</p>
<p>Ma passiamo ai modi in cui ti cambierà la vita.</p>
<h3>Ti farà sembrare vecchio qualsiasi posto di lavoro</h3>
<p>Lavori in una grande azienda? In una startup? In un piccolo ufficio? In una fabbrica? All'improvviso ti sembrerà di far parte di un'azienda del giurassico. Non sopporterai più l'idea di avere un capo che ti dice cosa fare e quanto ci devi mettere. A ogni richiesta inutile o assurda penserai "perché?" (forse é già così, ma questo libro rende insopportabile la domanda).</p>
<h3>Vorrai provare il lavoro da remoto</h3>
<p>Chi come me fa un lavoro che può essere svolto ovunque nel mondo, si chiede perché deve perdere ogni giorno 2 ore della propria vita in un autobus. In Rework viene descritto molto bene il perché: permette di scegliere i migliori talenti su scala globale senza obbligare a trasferirsi con moglie e figli al seguito. Purtroppo ci sono anche svantaggi nel lavorare da remoto, almeno secondo me, ma il libro non ne parla.</p>
<h3>Rework rifiuta il modello capitalistico delle startup</h3>
<p>Tutti a fare startup, ora che va di moda. Ma oltre alla startup, dove si scommette coi soldi degli altri, e le aziende tradizionali, dove si lavora per arricchire qualcun altro, il libro propone una terza via: un'azienda piccola "quanto basta" capace di soddisfare tutte le sue reali esigenze, e che cresce in maniera sostenibile. Anzi, aziende molto grandi possono sembrare più solide, ma lavorarci é di una noia mortale. Questo modello "sostenibile" é stato applicato in primo luogo su loro stessi quindi é realizzabile (senza cercare il profitto <em>esponenziale</em> ad ogni costo).</p>
<h3>Rework Rifiuta il marketing convenzionale</h3>
<p>Cosa pensiamo quando guardiamo una pubblicità? E quando una voce registrata dice "ci dispiace per l'attesa, il tempo medio di risposta é di 16 minuti"? Queste sono cose che fanno tutti, specialmente le aziende più grandi dove hanno un reparto marketing dedicato, e per un'azienda nascente non é pensabile entrare in competizione con loro.</p>
<p>Il libro propone molte rivoluzioni:</p>
<ol>
<li>Il prodotto che state realizzando non deve essere per forza completo; ma quello che fa lo deve fare bene e facilmente.</li>
<li>Il vostro reparto marketing siete tutti voi del team.</li>
<li>Inutile copiare le cose che fanno gli altri, vi faranno apparire come gli altri.</li>
<li>Una cosa che le grandi aziende non fanno, e che voi potreste fare, é <strong>insegnare</strong>.</li>
</ol>
<h3>"piccolo" ti sembrerà la giusta dimensione</h3>
<p>Come reggere un'azienda distribuita geograficamente che lavora con fusi orari diversi? Come fare in modo che i membri del team si scannino per far vincere l'idea migliore? (non quella che scontenta di meno). Bisogna creare l'ambiente adatto, l'ambiente che permette a tutti di esprimersi (provate a dire al vostro capo: hai detto una cazzata!) e di facilitare il dialogo.</p>
<p>Forse é così che quella sorta di utopia comunistica di 37signals ha creato un'azienda che fattura milioni di dollari e paga 75k all'anno (più bonus) i propri dipendenti... Che dire, provo a mandare il CV. Buona lettura.</p>
Vuoi imparare flexbox ma ti annoi? Prova questo gioco
2016-03-13T16:27:01Z
https://michelenasti.com/2016/03/gioco-flexbox/
<p>Tutti abbiamo sentito parlare di <strong>flexbox</strong>, <em>spero</em>, è una nuova spec di css3 che permette di organizzare gli elementi di una pagina web in maniera più semplice rispetto a prima. Nonostante sia una feature per così dire <em>moderna</em> è ben supportata da tutti i browser (anche mobile!).</p>
<p>Forse il motivo per cui è poco utilizzata è che tecnologie come Bootstrap risolvevano il 95% dei problemi che risolve flexbox; solo che bootstrap fa tutto "vecchia scuola".</p>
<p>Il problema di queste nuove "feature" è che bisogna impararle, e non c'è altro modo che studiarle (dal web), provarle, o ... giocarle.</p>
<p>E' proprio tramite un simpatico giochino (<a href="http://www.flexboxdefense.com/">flexbox defense</a>) che ho imparato i concetti base del flexbox model; Invito tutti ad andare sul sito e a provare ad arrivare all'ultimo quadro con il 100% dei punti fatti. (come me).</p>
<h3>Come si gioca</h3>
<p>Bisogna posizionare dei cannoni su una mappa per distruggere i nemici che percorrono il sentiero marrone. I cannoni non possono essere posti sui sentieri ma solo negli spazi verdi. Come posizionarli? Bisogna utilizzare il css e in particolare le proprietà flexbox suggerite quadro per quadro.</p>
<p>Curiosità: non avevo idea di come funzionassero i flexbox prima di oggi, ma grazie al giochino <strong>ora mi sento piuttosto sicuro di come funziona</strong>.</p>
<h3>display: flex</h3>
<p>è la property da dare a un div. Da quel momento si comporterà seguendo le regole del flexbox model.</p>
<h3>**justify-content</h3>
<p>Distribuisce gli elementi orizzontalmente in base alle regole specificate.</p>
<ul>
<li><code>flex-start</code>: Raggruppa gli elementi nella parte sinistra (l'inizio) del contenitore</li>
<li><code>flex-end</code>: Raggruppa gli elementi nella parte destra del contenitore</li>
<li><code>center</code>: Raggruppa gli elementi al centro del contenitore</li>
<li><code>space-between</code>: Distribuisce equamente gli elementi nel contenitore in modo ch il primo elemento si allinea a sinistra e l'ultimo elemento si allinea a destra</li>
<li><code>space-around</code>: Distribuisce equamente gli elementi nel contenitore in modo che tutti gli elementi abbiano lo stesso spazio intorno a loro</li>
</ul>
<h3>**align-items</h3>
<p>Distribuisce gli elementi verticalmente.</p>
<ul>
<li><code>flex-start</code>: Allinea gli elementi sul top del contenitore</li>
<li><code>flex-end</code>: Allinea gli elementi sul fondo del contenitore</li>
<li><code>center</code>: Allinea gli elementi al centro del contenitore</li>
<li><code>baseline</code>: Allinea gli elementi sulla baseline del contenitore</li>
<li><code>stretch</code>: Allarga gli elementi per riempire il container</li>
</ul>
<h3>**flex-direction</h3>
<p>permette di modificare la distribuzione spaziale degli elementi. Con <code>column</code> e <code>column-reverse</code> si invertiranno i comportamenti delle due proprietà: justify-content impilerà gli elementi verticalmente e align-items li organizzerà orizzontalmente.</p>
<ul>
<li><code>row</code>: Dispone gli elementi da sinistra a destra</li>
<li><code>row-reverse</code>: Dispone gli elementi da destra a sinistra</li>
<li><code>column</code>: Dispone gli elementi dall'alto verso il basso</li>
<li><code>column-reverse</code>: Dispone gli elementi dal basso verso l'alto</li>
</ul>
<h3>**Order</h3>
<p>La proprietà <strong>order</strong> può essere applicata ai singoli item e serve per modificarne la posizione all'interno della disposizione naturale. Ogni item inizia la numerazione da 0, quindi specificando -1 lo si sposterà più a sinistra (mentre dandogli +1 lo si sposterà a destra).</p>
<h3><strong>Align-self</strong></h3>
<p>E' sempre un attributo pensato per il signolo item. Ha le stesse proprietà (e comportamenti) di align-items, ma viene applicato all'elemento singolo (mentre align-items si applica a gruppi di oggetti).</p>
Come programmare in NodeJS, the right way
2016-03-23T09:46:28Z
https://michelenasti.com/2016/03/programmare-nodejs-the-right-way/
<p>Sono stato al workshop tenuto da <a href="https://www.linkedin.com/in/matteocollina">Matteo Collina</a> a Roma, che è da poco diventato uno dei Node Core Developer (tradotto: contribuisce al linguaggio e al runtime di NodeJS).</p>
<p>Il workshop aveva, come obiettivo, quello di realizzare delle API Rest con <a href="http://hapijs.com/">HAPI</a> e NodeJS. HAPI è un altro framework concorrente del più noto <a href="http://expressjs.com/it/">Express</a>.</p>
<p>Con Javascript non posso dire di essere l'ultimo arrivato, eppure ho incontrato molte difficoltà principalmente perchè non ho mai approcciato un framework di testing "serio" lato backend e poi perchè Matteo usa VIM ed è velocissimo. Molte volte non riuscivo a stargli dietro 🙂</p>
<ul>
<li>E' impossibile programmare in NodeJs senza realizzare <strong>test unitari</strong>. I test garantiscono che possiamo sopravvivere a un refactoring. Come framework di test Matteo ci ha suggerito "<a href="https://github.com/hapijs/lab">lab</a>", che funziona bene per il backend.</li>
<li>La <strong>struttura delle directory "standard"</strong> viene presa pari pari dal progetto <a href="https://github.com/nodejs/node">node</a> quindi creeremo un unico entry point che espone tutte le nostre funzionalità; nella cartella <code>lib</code> metteremo tutti i file .js che utilizziamo, mentre in <code>src</code> metteremo il codice nativo (C o C++). Ovviamente in <code>node_modules</code> ci saranno tutti i package esterni installati tramite <code>npm install -save</code>.</li>
<li>JS è molto libertino con le <strong>code convention</strong>, infatti anche i core developer di Node ne usano di diverse; <strong>non c'è uno standard</strong> <em>à-la-Java</em> da seguire. Ma se volete un tool che aggiusti il codice per voi, potete usare <code>standard</code> (<code>npm install standard -g</code>). Questo tool eliminerà tutti i punti e virgola, quindi se siete degli amatori del genere installate <code>semistandard</code> (da <em>semicolon</em>, punto e virgola in inglese).</li>
<li>Quanto è <strong>veloce programmare</strong> in Node? tantissimo. Non riesco a credere di aver scritto una API Rest con autenticazione, test e altre feature in 8 ore di lavoro. Spiegazioni incluse!</li>
<li>Oltre alla velocità di programmazione c'è anche <strong>velocità di esecuzione</strong>, infatti il single thread model di JS permette di avere un'elevata concorrenza e un'alta affidabilità.</li>
<li>Io personalmente penso che Node sia un candidato ideale per un'<strong>architettura a microservices</strong>, perchè se cambia un requisito si fa prima a riscrivere tutto che a modificare il sorgente.</li>
<li>Matteo ci ha raccontato di aver scritto un'app in Node che ha saturato il cavo di rete, nel senso che inviava più dati di quanti ne potesse reggere la macchina (qualcosa come 800 Gbit !)... Inoltre <strong>NodeJs richiede meno risorse</strong> (in termini di hardware) di altri linguaggi più strutturati.</li>
<li>Per cosa Node <strong>non va bene</strong>? Per le applicazioni finanziare o che comunque devono lavorare con i <strong>numeri</strong>, perchè Node ha solo i double e questo genera problemi di arrotondamento. Inoltre <strong>non supporta ancora la tail recursion</strong>, quindi potreste avere problemi con chiamate ricorsive estreme. Tutto questo potrebbe cambiare con le prossime versioni di Node (che hanno alcune feature sperimentali attivabili via <code>-harmony</code>).</li>
<li><strong>Niente minificazione</strong> o cose strane tipo webpack o babel su Node! Solo plain JS con quello che node mette a disposizione.</li>
</ul>
<p>Questi sono alcuni degli appunti che ho preso quel giorno, ci sono tanti altri aspetti che meriterebbero di essere approfonditi più tante storielle e aneddoti che Matteo ci ha raccontato, ma quelli meritano storie a sè! Buon JS a tutti 🙂</p>
L'importanza di un Pet Project spiegata da chi non ne ha mai realizzato uno
2016-04-04T18:14:18Z
https://michelenasti.com/2016/04/limportanza-di-un-pet-project-spiegata-da-chi-non-ne-ha-mai-realizzato-uno/
<p>Ieri ho letto questo bellissimo articolo, "<a href="http://blog.samaltman.com/advice-for-ambitious-19-year-olds">consigli per l'ambizioso diciannovenne</a>" (in inglese). In questo articolo si esaminano le tre strade che un 19enne può scegliere:</p>
<ul>
<li>
<p>andare all'università</p>
</li>
<li>
<p>iniziare una sua startup</p>
</li>
<li>
<p>andare a lavorare per un'azienda.</p>
</li>
</ul>
<p>Bene, ogni strada ha i suoi pro e i suoi contro, ma siccome siamo sul mio blog vorrei parlarvi della scelta che a suo tempo feci io: andare all'università.</p>
<p>L'Università é stata, per molti versi, la mia salvezza dato che fino a quel momento non ero molto bravo a scuola, anzi in verità non sopportavo lo studio di materie leggermente inutili come la filosofia o il latino.</p>
<p>All'Università ci stavo bene e mi piaceva studiare. I voti sono sempre stati molto proporzionali all'impegno che ci ho messo, quindi quando fai una cosa che ti piace e ci metti la passione i risultati arrivano. Ed é stato così che ho terminato il percorso universitario specialistico con 110&lode.</p>
<p>Eppure terminata l'Università avevo la sensazione di non saper fare nulla. Non avevo idea di come si lavorasse in un team, come si programmasse "seriamente" in un'azienda, ma anche su un progetto open source. Sembrava tutto alla mia portata, ma anche difficile e inarrivabile.</p>
<p>Dopo anni di lavoro posso dire con franchezza che il mondo del lavoro non é tutta questa esperienza. I problemi sono tanti, in primis il fatto che si lavora su progetti sostanzialmente noiosi (chi vorrebbe lavorare su un gestionale per farmacie?! Chi?!) e anche se sono progetti da milioni di euro, a te arrivano 1200€/mese (il mio primo stipendio netto).</p>
<p><em>Con un Pet Project puoi fare esperienza da subito</em></p>
<p>Le parti più belle del lavoro sono proprio la possibilità di <strong>lavorare con persone più brave di te</strong> e di <strong>sbattere la testa su problemi difficili (e risolverli)</strong>. In particolare il secondo punto mi appassiona, perché mi permette di fare un mucchio di ipotesi e di validarle tramite debugging e prove più o meno strampalate. È così che <strong>ho scoperto di essere più appassionato al frontend</strong> (e incidentalmente a javascript).</p>
<p>Eppure un modo di fare esperienza prima della laurea c'é. <strong>Cinque anni universitari rischiano di essere buttati a mare se li avete passati solo a imparare teoremi e non avete realizzato alcunché di interessante</strong>. L'Università é il luogo ideale in cui ci si può trovare un "problema da risolvere" e lavorare, anche creativamente, per risolverlo. Ero all'Università che ho pensato, solo per citarne due, a un "Uber prima di Uber" (da me fantasticamente chiamato Portamy) e a un'app per cellulare che mi permettesse di collegarmi al wifi Unisa (all'epoca bisognava loggarsi esplicitamente per fare ogni cosa, credo sia ancora così).</p>
<p>Purtroppo sono sempre stato uno di quelli che pensa "prima devo conoscere tutto sulle tecnologie che mi servono e poi posso lavorare", quindi non sono mai partito con questi progettini paralleli. Volevo farli bene, volevo farli con la giusta ingegneria. Non avevo neanche la consapevolezza che mi stessi facendo un torto da solo.</p>
<p><strong>Vorrei poter tornare indietro e iniziare lo sviluppo di qualcosa, qualsiasi cosa, su cui possa dire di averlo pensato, sbagliato, ma realizzato io</strong>; é una vera palestra, si capiscono molte dinamiche e le proprie inclinazioni, e magari ci si apre da soli una strada più felice di quella che potrebbe essere in una grande azienda.</p>
<p>In USA c'é un vero e proprio culto per i side projects, infatti i Venture Capitalists sono molto attratti da quelle applicazioni che <strong>risolvono problemi reali</strong> (quali problemi sono più reali di quelli che avete voi oggi?) o da soluzioni creative a problemi già affrontati (machine learning ai libri di ricette?). <strong>Lavorare in gruppo</strong> a questi progettini é un ulteriore plus: all'Università potete scegliervi il meglio dei colleghi, non subirli come nel mondo del lavoro.</p>
<p>Un altro punto a favore dei Pet projects é che, essendo voi neolaureati, <strong>nessuno si aspetta che siano "production ready" ma se l'idea é bella tanti verranno a bussare alla vostra porta,</strong> anche persone con la giusta esperienza, per portare il vostro progetto at the next level. (forse sto un pò esagerando, ma non troppo).</p>
<p>Alla fine degli studi, qualunque strada si decida di intraprendere, il vostro "Pet project" sarà una delle vostre medagliette da esporre orgogliosamente sul petto.</p>
Gestire le dipendenze frontend con... bower
2016-04-28T07:15:32Z
https://michelenasti.com/2016/04/gestire-le-dipendenze-frontend-con-bower/
<p>Avete un progetto web che include molte librerie esterne (angular, moment, jquery...)? Aggiornarle, gestirle e censirle sta diventando un problema? Nel corso degli anni sono usciti molti tool per la gestione delle dipendenze, e questo vale per ogni linguaggio di programmazione esistente al mondo; per il frontend una delle soluzioni più semplici da implementare è <strong>bower</strong> che con pochissimo sforzo si configura anche negli ambienti più ostici.</p>
<h1>Bower</h1>
<p><strong>Bower</strong> è un gestore delle dipendenze frontend, ossia gestice i file .js, e funziona in modo molto simile a Maven (se venite da tecnologie Java). Con <strong>bower</strong> è possibile definire un file (chiamato <code>bower.json</code>) in cui vengono inserite tutte le dipendenze della nostra webapp e gestirle con pochi comandi. Obiettivo? ridurre la complessità, semplificare gli aggiornamenti, vivere più felici con il proprio team 😀</p>
<p>Per installare bower come prerequisito bisognerà installare NodeJs, dal sito <a href="https://nodejs.org/en/">nodejs.org</a>.</p>
<blockquote>
<p><strong>Nerd Alert!</strong> per poter usare bower e/o Node vi servirà un po' di dimestichezza con il terminale. Tutti i comandi che vedrete qui andranno lanciati da linea di comando.</p>
</blockquote>
<p>Successivamente:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> <span class="token function">install</span> bower <span class="token parameter variable">-g</span></code></pre>
<p><code>npm</code> è un package manager di NodeJs, ossia un tool da linea di comando che permette di installare programmi, dipendenze, librerie, etc. Il comando <code>install</code> si preoccupa di scaricare il pacchetto specificato, <code>bower</code>, e con l’opzione <code>-g</code> stiamo dicendo che bower deve essere installato globalmente (dunque sarà disponibile da qualunque path del terminale).</p>
<p>Non mi addentro in ulteriori spiegazioni su npm, perchè può fare molto di più se lavorate con NodeJs (addirittura c'è chi lo usa al posto di bower!), prometto di ritornare su npm in futuro.</p>
<p>Una volta installato bower è molto facile iniziare ad utilizzarlo: dal sito <code>http://bower.io/search/</code> si potranno ricercare pacchetti (es. angular, moment…) e se ci interessano basterà digitare da linea di comando</p>
<pre class="language-shell"><code class="language-shell">bower <span class="token function">install</span> angular`</code></pre>
<p>Per installare la versione di angular più recente.</p>
<p>Se invece vogliamo installare una versione specifica, possiamo scrivere</p>
<pre class="language-shell"><code class="language-shell">bower <span class="token function">install</span> bootstrap<span class="token comment">#2.2</span></code></pre>
<h2>Usare Bower like a "pro"</h2>
<p>Quello che abbiamo visto fino ad ora riguarda dipendenze "one shot"; di solito noi vorremmo poter catalogare tutte le dipendenze della nostra webapp così da condividere con i colleghi solo il file delle dipendenze. Insomma, piuttosto che intasare SVN o Git con migliaia di file esterni, identici ad altri già presenti on line, scambiamo solo il file <code>bower.json</code> e chiediamo a bower di scaricare le dipendenze per noi.</p>
<p>Se vogliamo seguire questo approccio, lanciamo il comando <code>bower init</code> all’interno della cartella che contiene il nostro progetto, rispondiamo alle domande che bower ci fa (nome del package, autore..) e successivamente avremo uno scheletro di file bower bello e creato.</p>
<p>Ora che abbiamo creato un bower.json qualsiasi, possiamo scaricare e salvare una dipendenza usando il comando</p>
<pre class="language-shell"><code class="language-shell">bower <span class="token function">install</span> angular <span class="token parameter variable">--save</span></code></pre>
<p>che oltre a scaricare angular all'ultima versione disponibile, lo inserisce anche nel <code>bower.json</code>.</p>
<h2>Un caso pratico</h2>
<p>A lavoro ho creato un progetto "base" che dovrà servire come punto di inizio per le prossime webapp che andremo a sviluppare. Contiene principalmente angular, jquery e qualche altra libreria creata da noi (quindi è interessante vedere come integrare in bower librerie aziendali, private).</p>
<p>Per creare questo progetto ho per prima cosa creato un file di configurazione che si chiama <code>.bowerrc</code> (notate il punto all'inizio). Serve a dare istruzioni di carattere "generale" a bower. Non è obbligatorio se vi stanno bene le configurazioni di default. Eccolo:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"directory"</span><span class="token operator">:</span> <span class="token string">"lib"</span><span class="token punctuation">,</span><br /> <span class="token property">"timeout"</span><span class="token operator">:</span> <span class="token number">300000</span><span class="token punctuation">,</span><br /> <span class="token property">"proxy"</span><span class="token operator">:</span> <span class="token string">"http://localhost:9915"</span><span class="token punctuation">,</span><br /> <span class="token property">"https-proxy"</span><span class="token operator">:</span> <span class="token string">"http://localhost:9915"</span><span class="token punctuation">,</span><br /> <span class="token property">"no-proxy"</span><span class="token operator">:</span> <span class="token string">"localhost"</span><span class="token punctuation">,</span><br /> <span class="token property">"strict-ssl"</span><span class="token operator">:</span> <span class="token boolean">false</span><br /><span class="token punctuation">}</span></code></pre>
<p>Queste impostazioni sono tipiche in una realtà <em>enterprise</em> dove c'è un proxy da configurare. Provo a spiegare le più importanti:</p>
<ul>
<li><strong>directory</strong>: specifica in quale directory andare a scaricare le dipendenze esterne. Ad esempio, se importiamo angular, la troveremo in lib/angular .</li>
<li><strong>proxy</strong> e <strong>https-proxy</strong>: se avete qualche proxy impostato per accedere a internet, cosa assolutamente normale in ambienti aziendali, li configurate così.</li>
<li><strong>no-proxy</strong>: se ci sono host da raggiungere che fanno parte della rete locale, e che dunque non devono passare per il proxy, si possono specificare qui.</li>
<li><strong>strict-ssl</strong>: se il vostro SVN o Git privato è raggiunto in modalità https, è molto probabile che il certificato non sia validato da un'autorità riconosciuta. settando questo flag a "false", bower ignora i controlli di validità sul certificato.</li>
</ul>
<p>Questo file <code>.bowerrc</code> può andare sia nella home dell’utente sia nella home del progetto, dato che sono impostazioni globali. Bower quando va sui repo cerca il file <code>.bowerrc</code> più vicino andando a vedere prima nella directory corrente, poi in quella superiore, etc. fino alla home utente.</p>
<p>Superata questa prima fase di configurazione, che farete una sola volta, vediamo il file <strong>bower.json</strong> che contiene la vera e propria lista delle dipendenze:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"MyProject"</span><span class="token punctuation">,</span><br /> <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"0.0.1"</span><span class="token punctuation">,</span><br /> <span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> ...<br /> <span class="token property">"jquery"</span><span class="token operator">:</span> <span class="token string">"~2.1.3"</span><span class="token punctuation">,</span><br /> ...<br /> <span class="token property">"jquery-scrollintoview"</span><span class="token operator">:</span> <span class="token string">"litera/jquery-scrollintoview#06834cf7fdba0e86cac84ed7761ea64a3a5fbec8"</span><span class="token punctuation">,</span><br /> ...<br /> <span class="token property">"mylib"</span> <span class="token operator">:</span> <span class="token string">"svn+https://&lt;mysvnrepo&gt;/frontend-libs/mylib#0.0.1"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"private"</span><span class="token operator">:</span> <span class="token boolean">true</span><br /><span class="token punctuation">}</span></code></pre>
<p><code>name</code> e <code>version</code> sono due attributi utili a bower per poter cacheare il pacchetto. <strong>private</strong> invece serve a evitare che il pacchetto venga committato per errore su bower central.</p>
<p>all’interno delle dipendenze vediamo i 3 tipi di dipendenze che ho utilizzato:</p>
<ol>
<li><strong>jquery</strong> è uno di quei pacchetti presenti nel bower globale (raggiungibile a <a href="http://bower.io/search">bower.io/search</a>), quindi è specificato solo per versione;</li>
<li><strong>jquery-scrollintoview</strong> è un plugin di jquery e non esiste su bower central, ma solo su GitHub. Scrivendo <code>litera/jquery-scrollintoview</code> lo stiamo andando a prendere da github. La versione è la <code>#06834cf7fdba0e86cac84ed7761ea64a3a5fbec8</code> (che sarebbe l’hash del commit su github). Quando una dipendenza non ha versioni, è possibile fissare il download ad un commit specifico in questo modo.</li>
<li><strong>mylib</strong> non è una dipendenza presente nè su bower nè su github, perchè nostra, quindi ho specificato la sua versione con il path su SVN. Se il vostro repository inizia con http o https, bisogna aggiungere <code>svn+</code> davanti all'indirizzo, altrimenti bower assume che sia un repository git. Se il nostro progetto è versionato in maniera standard, ossia con le cartelle <code>branches</code>, <code>tags</code>, e <code>trunk</code>, bower sceglierà la versione giusta andando a vedere se esiste un cartella in tag con quel nome. Dunque nel nostro caso andrà all’interno della cartella <code>tags</code> presente su svn e poi prende il contenuto della cartella <code>0.0.1</code> e farà il checkout da <code>svn+https://<mysvnrepo>/frontend-libs/mylib/tags/0.0.1/</code>.</li>
</ol>
<p>Una volta che abbiamo specificato tutte le dipendenze che abbiamo, possiamo semplicemente lanciare un fantastico <code>bower install</code> dalla root del progetto e bower si preoccuperà di scaricare tutte le dipendenze per conto nostro. Provare per credere!</p>
<h3>Nel prossimo articolo: private-bower</h3>
<p>In molte realtà aziendali, dipendenze esterne vengono versionate anche su server privati dell'azienda, o per renderne più veloce lo scaricamento, o perchè sono stati corretti dei bug in librerie open dunque le si vuole gestire internamente.</p>
<p>Con git e svn bower è già compatibile con la stragrande maggioranza dei repo globali, ma resta il fatto che bisogna scrivere in bower.json il path completo dei repository. Questa cosa fa un po' storcere il naso ai puristi che vorrebbero gestire queste librerie in maniera trasparente (ossia, io ti dico solo la versione, e tu la vai a prendere dal mio server bower). Questo problema lo risolve un package chiamato <code>private-bower</code>, di cui spero di riuscire a parlare in un prossimo articolo (scrivere questo mi è costato 3 settimane, per mancanza di tempo!). Spoiler: facciamo puntare <code>bower</code> da riga di comando al nostro server <code>private-bower</code>, che cercherà i pacchetti per noi; se non li trova li va a scaricare da <code>bower.io</code> (il server centrale). Non resta che vedere come farlo.</p>
<p>Buona bowerizzazione dei vostri frontend!</p>
<p>Altre risorse:</p>
<ul>
<li><a href="http://bower.io/#getting-started">Bower Getting Started</a></li>
</ul>
Visual Studio Code, l'editor che mancava per JS e Node
2016-05-27T08:28:39Z
https://michelenasti.com/2016/05/visual-studio-code-leditor-mancava/
<p><strong>Visual Studio Code</strong> <strong>è l'editor/IDE Javascript definitivo</strong>. Lo so, mi sto sbilanciando, la concorrenza è tanta, ma ha alcune <em>feature</em> essenziali a costo quasi zero. E pensare che è sviluppato e sponsorizzato da Microsoft ! Chi l'avrebbe mai detto 5 anni fa?</p>
<p>Ecco una guida su come installarlo per le mie esigenze:</p>
<h3>Scaricare Visual Studio Code</h3>
<p>Ovviamente <a href="https://code.visualstudio.com/">scaricarlo dal sito ufficiale</a> (per Windows, Mac, Linux...)</p>
<h3>EsLint</h3>
<p>Installare <a href="http://eslint.org/">EsLint</a> e il suo plugin per VS. EsLint è un un syntax checker e inoltre permette di evitare una serie di bad practice riconosciuti dalla community nel corso degli anni. Per installare EsLint globalmente, se non l'avete mai installato:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> <span class="token function">install</span> eslint <span class="token parameter variable">--global</span></code></pre>
<p>A questo punto recatevi presso la directory del vostro progetto su cui volete eseguire EsLint e lanciate il comando</p>
<pre class="language-shell"><code class="language-shell">eslint <span class="token parameter variable">--init</span></code></pre>
<p>Ora che abbiamo installato il tool, integriamolo col nostro editor: in VS lo si fa aprendo la finestra dei comandi rapidi (<code>Cmd + P</code>) e scrivendo <code>ext install vscode-eslint</code>. Riavviate Visual Studio Code se non volete comportamenti strani.</p>
<p>(Ho riscontrato a mie spese che VS non richiede il riavvio dopo l'installazione di plugin, però poi si incarta se non lo fai. Quindi fatelo.)</p>
<h3>La Console</h3>
<p>Per aprire il terminale nella directory corrente basta un semplice <code>Cmd + Shift + C</code>.</p>
<h3>Typings</h3>
<p><a href="https://github.com/typings/typings">Typings</a> è un package di Node che permette di avere un aiuto contestuale man mano che scriviamo. Dal sito web non si capisce benissimo, è scritto in maniera piuttosto oscura, ma è un enorme raccoglitore di API per tutte le librerie JS più diffuse. Se siete programmatori Java, VS sta provando a fare quello che faceva Eclipse con i Javadoc.</p>
<p>Fate una prova ora che typings non l'avete ancora installato. Provate a creare un file in VS scrivendo:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />fs<span class="token punctuation">.</span> <span class="token comment">// <-- a questo punto si apre una finestra contestuale con ... niente</span></code></pre>
<p><img src="https://i0.wp.com/michelenasti.com/uploads/2016/05/senza-typings.jpg" alt="Senza Typings, Visual Studio prova a dare un aiuto, ma non ci riesce veramente: Infatti tutto quello che viene mostrato in console non serve a nulla." /></p>
<p>Bene, proviamo ad aggiustare questo comportamento. Facciamo sì che VS ci restituisca suggerimenti utili quando proviamo a usare metodi dei nostri oggetti. Per farlo installeremo Typings:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> <span class="token function">install</span> typings <span class="token parameter variable">--global</span></code></pre>
<p>poi dobbiamo installare le definizioni di Node:</p>
<pre class="language-shell"><code class="language-shell">typings <span class="token function">install</span> dt~node <span class="token parameter variable">--global</span> <span class="token parameter variable">--save</span></code></pre>
<p>Manca un passaggio nascosto, ossia bisogna cliccare sull'icona a forma di lampadina in basso a destra, per creare un file di configurazione dell'IDE, che permette di abilitare la <em>magia</em>.</p>
<p>|<a href="https://i0.wp.com/michelenasti.com/uploads/2016/05/visual-studio-magic.jpg">cliccando su quest'icona verrà creato un file di configurazione dell'IDE, voi non dovete fare nulla se non salvarlo e riavviare.</a>|</p>
<p>Riavviate Visual Studio, che a questo punto è già configurato per funzionare con Typings, <em>et voilà</em>!</p>
<p><img src="https://i0.wp.com/michelenasti.com/uploads/2016/05/con-typings.jpg" alt="un esempio di suggerimento contestuale che è davvero d'aiuto." /></p>
<p>C'è tutto un mondo di altre opzioni e possibilità da esplorare con VS, ad esempio Git, o l'esecuzione in debug di app, ma il tempo è tiranno come al solito quindi ne parleremo in futuro! 🙂</p>
Flow e Typscript: Type checking per Javascript
2016-06-06T18:00:46Z
https://michelenasti.com/2016/06/flow-typscript-type-checking-javascript/
<p>ho trovato questo <a href="http://djcordhose.github.io/flow-vs-typescript/2016_hhjs.html#/">interessantissimo link</a> sulla differenza tra <strong>Flow</strong> (di Facebook) e <strong>Typescript</strong> (di Microsoft). Vi consiglio di dargli uno sguardo perché sono slide che si capiscono (rarità!).</p>
<p>Entrambi sono ottimi tool che permettono di portare il vostro codice JS a un altro livello.</p>
<p><em>Flow</em> è un type checker "offline", nel senso che è un tool da linea di comando che controlla se avete commesso errori di tipo (es. assegnare un numero a una stringa). Ovviamente esistono plugin per i migliori editor, e tante altre facilities (babel,, webpack...)</p>
<p><em>Typescript</em> invece è un vero e proprio linguaggio, superset di JS; questo codice viene poi compilato in JS con tutti i crismi e i carismi. Anche qui c'è tanto supporto da parte dei tool.</p>
<p>La differenza più importante però ve la dico io. <strong>Flow non è (ancora) supportato su Windows</strong>. Esistono port non ufficiali, ma ci vuole ancora un po' per un supporto completo.</p>
<p>Avrei voluto utilizzare / provare Flow, proprio perchè posso integrarlo man mano che ne ho bisogno nel codice già scritto, piuttosto che Typescript che invece richiede proprio di ridisegnare il proprio build set. E qui a lavoro ho solo una macchina Windows.</p>
<p>In ogni caso, <em>sono entrambi grandissimi pezzi di software</em> che <em>semplificano la manutenzione di codice complesso</em>, quindi vi consiglio di usare il migliore dei due per i vostri compiti, soprattutto se avete una webapp complessa da cui dipende la vostra vita lavorativa.</p>
Cambio Lavoro
2016-06-23T18:41:54Z
https://michelenasti.com/2016/06/cambio-lavoro-alla-scoperta-del-remote-working/
<p>Big news nella mia vita! Ho cambiato lavoro!</p>
<p>Siete curiosi di sapere come, perchè, quando, dove, ma dovrete attendere un po' per tutti questi dettagli. Li sto ancora metabolizzando anch'io.</p>
<p>Volevo spendere qualche parola sul posto che ho appena lasciato, e provare a rispondere in parte a "perchè"..</p>
<p>Sono stato due anni fantastici in Engineering, e alla fine le persone che hai intorno diventano parte della tua famiglia. Anzi, le vedi più della tua famiglia, motivo per cui cerchi di andarci d'accordo più che puoi. Nel mio caso non mi sono dovuto sforzare poi molto, erano tutte persone decisamente ragionevoli 🙂</p>
<p>Quando ho deciso di cambiare lavoro sono stato il primo a domandarmi: ma come, lasci un lavoro fisso, in un posto tranquillo, con garanzie di futuro, etc.</p>
<p>Ammetto che non é la prima volta che faccio un salto del genere, da qualcosa di "sicuro" a qualcosa di leggermente più rischioso. Ma c'erano alcune cose che volevo urgentemente sistemare, prima che diventassero un problema.</p>
<h3>Viaggiare per lavorare</h3>
<p>La vecchia azienda non era proprio in un luogo felice. Dentro si, ma fuori no. Situata in una delle zone più complesse e industriali di Napoli, nel bel mezzo del nulla, per raggiungerla impiegavo circa un'ora di autobus ogni giorno (e poi ancora un'ora al ritorno). Dunque mi svegliavo presto e ritornavo tardi, soprattutto questo tempo mi sembrava profondamente sprecato.Per questo, uno dei requisiti che ho cercato nel prossimo lavoro é di limitare il più possibile gli spostamenti.</p>
<p>Metteteci poi che quelle poche volte che andavo in auto mi hanno rubato lo stereo (danno di circa 180€) e i copricerchi (danno di 80€)...</p>
<p><em>"Solo un'ora? Firmerei per poter lavorare a un'ora d'autobus!"</em> - a me non stava più bene, non vuol dire che valga lo stesso per voi. Ho conosciuto gente che veniva a lavorare da ben più lontano e andava bene così ...</p>
<h3>Stimoli</h3>
<p>Gli ultimi due anni li ho passati a conoscere profondamente il mercato dei software sanitari italiani, e ho visto da vicino le difficoltà di chi sviluppa e di chi usa questo tipo di SW che sono estremamente specialistici - si interfacciano con macchinari, stampano etichette, etc.. Non è esattamente roba per startup che vogliono _disruptare _il mercato, visto che servirebbe una conoscenza specialistica notevole.</p>
<p>Tuttavia c'è una costante che mi ha accompagnato nei primi quattro anni della mia carriera: sono sempre stato un "operaio" più che un progettista. In genere c'era sempre qualcuno che mi diceva cosa fare, e i margini di miglioramento / ottimizzazione di una funzionalità erano piuttosto limitati. Roba come interazione, design, ottimizzazione del backend, misurazione degli eventi dell'utente, sono tutte tematiche di cui ci si interessava poco. Perciò uno dei requisiti che avrebbe dovuto avere il mio prossimo lavoro è di darmi un grande margine di intervento su ciò che avrei fatto.</p>
<h3>Uscire dal mondo "ONLY JAVA"</h3>
<p>Da mesi sto approfondendo Javascript e tutte le tecnologie correlate - react, angular, nodeJS... . Ho addirittura fondato un <a href="http://michelenasti.com/2016/02/javascript-meetup-a-salerno/">meetup che sta avendo un discreto successo</a> (pur rimanendo nei limiti Salernitani).</p>
<p>Ben conscio che nessun linguaggio è quello definitivo, ho notato che a Napoli una volta entrati in una fetta di mercato (inizi come sviluppatore Java? sarai sviluppatore Java a vita) è difficile uscirne. Inoltre Java è uno dei linguaggi più diffusi al mondo, quindi perchè provare a cambiare?</p>
<p>Secondo me non è il _miglior_ linguaggio del mondo, ha qualche pecca, anche se ammetto che è quello che conosco meglio e di cui ho esplorato maggiormente la vastità delle sue possibilità. Con Java ci ho fatto di tutto, dalle interfacce Swing, a interfacce web con GWT, ho esposto servizi SOAP e poi anche REST, insomma java java java ...</p>
<p>Un altro requisito che mi ero imposto era di spaziare il più possibile in un'altra direzione rispetto a Java. In fondo, <strong>programmare mi piace ancora tantissimo</strong>.</p>
<h3>Per finire ...</h3>
<p>Me ne sono andato venerdì 17, non proprio il miglior giorno per salutarsi 😅 . Giocava Italia-Svezia, era la pausa tra primo e secondo tempo. Ho chiesto di uscire due ore prima, come non accontentare la richiesta di un ormai ex dipendente? Così, mentre Conte pensava a quali sostituzioni effettuare nella nazionale per rafforzare la squadra, io uscivo da Eng non senza emozioni, salutando tutti calorosamente.</p>
<p>Neanche il tempo di pensarci che la nuova avventura è già iniziata. Si ricomincia!</p>
Come scrivere un annuncio di lavoro efficace (soprattutto quando hai poco budget)
2016-06-25T11:22:11Z
https://michelenasti.com/2016/06/come-scrivere-un-annuncio-di-lavoro-efficace/
<p>L'articolo sul mio <a href="http://wp.me/p5zVKm-bk">cambio lavoro</a> ha riscosso qualche successo (wow!), e qualcuno mi ha chiamato per sapere dove fossi finito, che stessi facendo, etc.</p>
<p>In particolare una persona, dopo avermi contattato per farmi gli auguri, mi ha chiesto un aiuto: per il suo studio di comunicazione deve trovare uno <strong>sviluppatore full stack con caratteristiche “senior”</strong> ma non ha risorse, per così dire, infinite.</p>
<p>Così mi chiede: <em>puoi aiutarmi a trovare uno sviluppatore con esperienza in [X, Y, Z]?</em> Puoi aiutarmi a scrivere un annuncio di lavoro efficace? <em>Sono disposto a dargli una certa cifra [molto bassa rispetto al mercato].</em></p>
<p>Sarete d'accordo con me che <em>senior</em> e <em>cifra bassa</em> sono due cose che non vanno d'accordo.</p>
<p>Inoltre l'annuncio di lavoro era il solito mappatiello di sempre: cerchiamo sviluppatori che conoscano Java, JavaScript, Html5, Python, Ruby, SEO, etc (la mia descrizione non é la stessa dell'annuncio ma avete capito che si cercava un po' di tutto e anche oltre).</p>
<p>A questo punto ho fatto notare al mio amico che, se cerca una figura del genere con un budget limitato, deve cambiare almeno un paio di cose.</p>
<p>Piuttosto che un senior (che costa tanto) punterei su un giovane capace: in Yahoo dicono di assumere non quelli che già conoscono le tecnologie più usate, bensì quelli che hanno ben capito le basi, così che dopo saranno capaci di imparare qualunque cosa con poco sforzo.</p>
<p>Inoltre un neolaureato (o comunque una persona alle prime armi) é più disposta a fare esperienza per uno stipendio più basso, in cambio però di un'esperienza di alto livello con le tecnologie più fighe. Tutto questo con la promessa che se diventa davvero bravo gli viene dato un aumento per allinearsi agli standard di mercato.</p>
<p>Questo mio amico ha una web agency che non fa lavori banali; scrivere un annuncio richiedendo un elenco di tecnologie é il modo migliore per far scappare qualunque aspirante. Io avrei riscritto l'annuncio così:</p>
<blockquote>
<p><em>ciao, siamo la Web Agency [...]. Ci occupiamo di questo e quello, siamo specializzati in sviluppo web ma anche di applicazioni mobile. I nostri clienti vogliono app belle da vedere, rapide e funzionali. Siamo sempre attenti agli ultimi trend e cerchiamo di offrire a chi lavora con noi la possibilità di imparare le ultime tecnologie del settore.</em></p>
</blockquote>
<blockquote>
<p><em>Stiamo cercando un giovane, anche alla prima esperienza, che voglia mettersi in gioco e aiutarci nel soddisfare i nostri clienti. Se pensi che il mondo del web ti piaccia, se pensi che le nuove tecnologie siano migliori delle vecchie, scrivici!</em></p>
</blockquote>
<blockquote>
<p><em>Offriamo: il tempo di crescere su determinate tecnologie + [budget annuale lordo].</em></p>
</blockquote>
<p>Ecco, un annuncio del genere non lo vedrei male. Io sono per pagare bene il talento, ma quando ti offrono la possibilità di imparare qualcosa di nuovo che poi puoi rivendere anche ad altri, un minimo di compromesso soprattutto a inizio carriera ci può stare. É così importante aggiornarsi sempre, che se un programmatore non lo fa rischia di trovarsi fuori mercato alla prossima rivoluzione tecnologica (in genere ce n'é una ogni 10 anni, l'ultima é stata quella delle app... Quale sarà la prossima? Forse é il Cloud?)</p>
<p>E con questo passo e chiudo da Biella. Ciao!</p>
Progetti a cui lavoro: HYPE - carta di credito, conto bancario, app. 3 in 1!
2016-07-17T15:13:25Z
https://michelenasti.com/2016/07/progetti-a-cui-lavoro-hype-carta-di-credito-conto-bancario-app-3-in-1/
<p>Uno dei prodotti su cui lavora la mia azienda é <strong><a href="https://www.hype.it/Hype/index.jsp">Hype</a></strong>.</p>
<p><strong>Hype</strong> é molte cose: una carta di credito con IBAN, quindi é anche un conto bancario, e si gestisce tramite un'app che ha delle funzionalità avanzate.</p>
<p>"Tutto qui? Ce ne sono centomila come Hype!" vero, e siccome io non vivo nelle caverne e maneggio soldi e carte di credito sfortunatamente più per spendere che per incassare, ecco la mia opinione a riguardo.</p>
<blockquote>
<p>Ovviamente si dovrebbe aver capito che lavoro per loro, anche se non lavoro direttamente su Hype; se non fossi stato convinto della bontà del progetto non ne avrei mai parlato spontaneamente.</p>
</blockquote>
<p>Hype é un progetto in cui crediamo molto, nato da una collaborazione tra <strong>Banca Sella</strong> e noi di <strong>Bemind</strong>.</p>
<p>Innanzitutto é <strong>totalmente gratuito</strong>, nella sua versione <em>starter</em>.</p>
<p>La registrazione e l'attivazione si può fare dalla app per cellulare, e ci siamo inventati un metodo super fico per evitare di farvi firmare cataste di documenti (tipici dell'ambito bancario) e poi spedirli: un Selfie con un documento di riconoscimento, e voilà, la carta sarà a casa vostra nel giro di un paio di giorni.</p>
<blockquote>
<p>l'attivazione della carta con il selfie l'ha introdotta anche Unicredit, ma poi c'è sempre un passaggio in banca per poterla attivare. +1 per noi!</p>
</blockquote>
<p>Nel frattempo, una volta approvata la richiesta di apertura del conto (in genere 10 minuti durante l'orario di ufficio), potete già utilizzare l'app da cellulare che é funzionante all 100% e con la quale potrete <strong>effettuare bonifici</strong>, <strong>inviare soldi</strong>, <strong>ricaricare</strong>, etc.</p>
<h2>Funzionalità ovvie</h2>
<p>Nella sua versione base, Hype é una carta ricaricabile e un conto bancario. Quindi può essere ricaricata via bonifico (ci vogliono 1-3 giorni...) o via carta di credito (istantanea). Le ricariche sono gratuite.</p>
<blockquote>
<p>controlate sempre la data in cui ho scritto questo articolo e verificate che le condizioni contrattuali siano quelle che descrivo io.</p>
</blockquote>
<p>Le spese di prelievo sono <strong>gratuite dai Bancomat</strong>.</p>
<p>Stando ai foglietti promozionali, <strong>sono gratuiti anche i prelievi dai Postamat</strong>, ma questo non l'ho ancora provato / verificato.</p>
<p>Potete fare bonifici, pagare on line, o nei negozi, come se fosse una normale carta di credito prepagata.</p>
<h2>Killer Features</h2>
<p>Con "killer features" si intendono quelle funzionalità che, una volta provate, diventano irrinunciabili.</p>
<p>Potete <strong>trasferire soldi tra carte Hype in maniera istantanea e senza costi</strong>, e i soldi vengono ricevuti istantaneamente (un bonifico con gli steroidi!). Immaginate ora quando andate in pizzeria e dovete dividere la spesa con la vostra comitiva... Non sarà più un incubo.</p>
<blockquote>
<p>Postepay, ad esempio, è uscita adesso con questa funzionalità che noi avevamo già da un anno. E a noi è gratis.</p>
</blockquote>
<p>Un'altra funzionalità indispensabile, per lo meno per me che sono uno spendaccione, sono gli <strong>obiettivi</strong>. E' possibile impostare un'obiettivo (Es. assicurazione dell'auto) e una data di scadenza (es. gennaio 2017), un importo, e Hype <strong>provvederà a "nascondere" una parte del credito</strong>, ad esempio 2,5€ al giorno, così da arrivare all'obiettivo coi soldi risparmiati senza sforzi.</p>
<blockquote>
<p>Che ve lo dico a fare .... ci hanno copiato anche questa funzionalità!</p>
</blockquote>
<p>Altra funzionalità per chi è stato vittima di frodi telematiche: <strong>la carta è attivabile e disattivabile a piacimento,</strong> tutte le volte che si vuole. E' possibile bloccare solo gli acquisti online, o solo quelli fisici, in modo da evitare che un malintenzionato possa divertirsi con i nostri soldi.</p>
<p>Per questo Hype è diventata la mia carta di riferimento per gli acquisti on line, e la attivo solo quando devo fare un acquisto, così sono sicuro che nessuno può fare macelli a mia insaputa.</p>
<h2>Tutto qui?</h2>
<p>Assolutamente no! Con l'appoggio di Banca Sella, sarà possibile pagare con hype su internet (tramite il loro gateway di pagamento, <a href="https://www.gestpay.it/">Gestpay</a>) e nei negozi, senza neanche tirare fuori la carta di credito. Questo è particolarmente utile per i commercianti che vogliono offrire più possibilità di pagamento ai propri clienti, oltre ad avere alcune statistiche sulla propria attività (e i propri competitor...). Maggiori dettagli sul sito di <a href="https://business.hype.it/site/">Hype for business</a>.</p>
<p>Spero di avervi convinto. <strong>La carta è perfetta per quei giovani che hanno bisogno di una carta per gestire i loro risparmi e le loro vacanze</strong>, ma anche per chi ha bisogno di <strong>trasferire soldi</strong> con i propri familiari e amici.</p>
<p>Attivare la carta è questione di minuti e, considerato che è gratuita, conviene farlo!</p>
My very personal Javascript Fatigue: the truth about JS testing
2016-08-30T23:34:08Z
https://michelenasti.com/2016/08/my-very-personal-javascript-fatigue/
<p>Trying to write this post as a 2007 blog post: personal, not so politically correct. (I'm not involved in JS politics by the way)</p>
<p>Well, In 2007 I was already studying <strong>Javascript</strong>, <strong>HTML</strong> and <strong>CSS</strong>, and I vividly remember that I <strong>HATED</strong> the web world because different browsers were doing crazy stuff for the same instruction. These were the good ol' <em>HTML 4</em> days.</p>
<p>I was pretty wrong. Browser apps are everywhere.</p>
<p>I have worked with a lot of javascript till now, I have also started a <a href="http://michelenasti.com/2016/02/javascript-meetup-a-salerno/">Javascript Meetup</a> here in my home town, Salerno. I have mentored and teached people about NodeJS. I have explained the story of the <em>single thread</em> and the differences between browsers and servers ... and still, there was something that I was missing.</p>
<h3>Everybody talks, nobody really does it: TESTING</h3>
<p>It's very easy to say that <strong>testing is important</strong>, it's just three words nonetheless. I have heard this a thousand times, from a bazillion of people. However, when I have asked to talk to me honestly about javascript testing, the answer was more or less:</p>
<blockquote>
<p><em>nobody really does testing</em></p>
</blockquote>
<p><strong>WTF?</strong> I have heard this from people that I respect, and at many companies where I was interviewed. Why?!</p>
<blockquote>
<p><em>Our apps are not so big that you can't test them manually</em></p>
</blockquote>
<p>Well, this can be an explanation, but not a great one.</p>
<blockquote>
<p><em>I can't find any resources about testing in javascript</em></p>
</blockquote>
<p>Uhm ... we are reaching the point ...</p>
<blockquote>
<p>**Testing in javascript is f***ing difficult</p>
</blockquote>
<p>Now that's the truth, ol' boy. Let's dive into it.</p>
<p><a href="https://i0.wp.com/michelenasti.com/uploads/2016/08/testingdifficult.jpg">Testing is difficult</a></p>
<h3>My personal testing jurney</h3>
<p>I decided to revamp a very old project, written in Java and Swing (yes, it was a standalone application, that's how computers worked in 2010). In 2016, since browsers can do better things than before, I have decided to rewrite everything in javascript and to write the frontend in something that is more mantainable.</p>
<p>I decided to write my stuff in angular 1 (I know it very well and since I have no spare time this was the only option for me). As a build tool, i have decided to use <a href="http://gulpjs.com/">Gulp</a> + <a href="http://browserify.org/">Browserify</a>. For those who don't know what Browserify is, well, it's a JS library that lets you import other javascript modules exactly like NodeJs (so, you would use <code>require('./TimerService')</code> to import it in your file). This has given me the ability to decouple angular from the actual JS code. Another advantage is that I can test it better, since it's only Javascript.</p>
<p>What about Gulp? Gulp is a build tool that can do a lot of stuff for you. I use it to trigger actions everytime I save a file, like running a syntax checker and convert my ES6 code to more <s>archaic</s> compatible ES5.</p>
<p>I also run my unit tests everytime I hit <code>Ctrl+S</code> (save).</p>
<h3>How do you write unit tests in Javascript?</h3>
<p>It would be great to just say, "use this!" and we're happy. In Javascript there are many libraries that are competing in the same space that there is no clear winner. I had to choose something that was well supported, well documented and used. I don't know if they are the best for every scenario, but since my time is limited, I am using these because <s>they were the first result on Google</s> they worked at the first attempt.</p>
<p>One library? NO! In JS, at least for browser testing, you need:</p>
<ul>
<li>a test runner, like <a href="https://karma-runner.github.io/1.0/index.html"><code>karma</code></a></li>
<li>a library to actually write the tests, like <a href="https://mochajs.org/"><code>mocha</code></a></li>
<li>an assertion library, like <a href="http://chaijs.com/"><code>chai</code></a></li>
</ul>
<p>WTF, again? 3 libraries to do something that <code>JUnit</code> (for Java) does alone ?? This is an <strong>example of the over-populated, github-based, quality-variegated NPM package manager</strong>.</p>
<p>These 3 pieces of software still don't have many years of maturity on their shoulders, and are not immune to bugs (which are promptly resolved, however). I have found one in mocha, for example, and I could not figure out until I upgraded.</p>
<p>Once you understand how to use these three libraries, then you have to actually write tests for your application: I don't have words to describe how difficult is to test a function that uses multiple <code>setTimeout()</code> in the code (it's an audio player, and deals with timing...). And now my next step in this journey is to mock stuff.</p>
<p>Another discovery I want to share with you (well, it's not a discovery, since it's on <code>chai</code> 's web page): you can check the test correctness in two ways, the <code>assert</code> way (like the one used in JUnit), or via a "behavioural" testing definition, something like "<code>expect(this).to.be.greaterThan(that)</code> ". two syntaxes for the same thing.</p>
<h3>Moral of this <s>story</s> rant</h3>
<p>Do we really need testing in Javascript? I believe that yes, <strong>testing is fundamental as the application grows</strong>, and expecially if you want to do some refactoring that has some sense.</p>
<p><strong>Should it be this difficult to test stuff? No.</strong></p>
<p>At some point in the future, testing JS will be easy like every other language in the programming world. Modern languages born with testing capabilities built in, and Javascript is so old that testing was just not cool to think about it.</p>
<p>So, if you believe that your application will be refactored some time in the future, if you don't want to become crazy, test it. <em>Test it now</em>. Run tests every <code>Ctrl+S</code> . This is the only way to be sure that things will not break up. Js is a dynamic language, it's not statically typed, and IDEs do not do type checking for you. Help yourself with testing.</p>
<p>And finally, **when testing will be cool again, you can say you were already doing it when stuff was being developed.**You can talk like a good, ol' Testing Granpa.</p>
How to log a PHP Soap call with NuSoap
2016-09-13T19:30:47Z
https://michelenasti.com/2016/09/to-log-php-soap-call-with-nusoap/
<p>I am doing some debugging in PHP for <strong>Gestpay</strong>.</p>
<p><a href="http://www.gestpay.it/"><strong>Gestpay</strong></a> is an italian system that allows to accept payments from customers, all over the world. If you have an e-commerce and want to try something (in my opinion) way more powerful than other systems, you should definitely check this out.</p>
<p>So, today I had to send a php SOAP call to Gestpay, but for some reasons I was doubting that the actual xml contained exactly what I was sending.</p>
<p>We are using <a href="https://sourceforge.net/projects/nusoap/">NuSoap</a>, a PHP library that allows to use SOAP on old versions of the language, as old as 4.x, and that does not have any external dependencies, so it should work with a great number of hostings. You may feel better using something more powerful and modern.</p>
<p>Back to our question: how do you show the NuSoap call?</p>
<p>Here it is:</p>
<p><div class="gist-oembed" data-gist="musikele/77cf3cf81cd9db9bd0bdac8003093bbe.json">
</div></p>
<p>And you'll see something the output of your code to something like ...</p>
<p><div class="gist-oembed" data-gist="musikele/7e50d19bb1a161704b6bff6c70019875.json">
</div></p>
<p>I found this tip on <a href="http://stackoverflow.com/questions/3606239/how-do-i-view-the-raw-xml-output-from-nusoap">stack overflow</a>... I'm sticking it in my blog because I'm sure I'll need it in the future!</p>
Com'è lavorare da remoto?
2016-09-18T18:23:43Z
https://michelenasti.com/2016/09/come-lavorare-da-remoto/
<p>Prima di parlarvi di come lavoro da remoto, se non l'avete ancora fatto, leggete la prima parte: <a href="http://michelenasti.com/2016/09/come-ho-cambiato-lavoro-prequel-di-come-lavorare-da-remoto">per chi lavoro e cosa faccio</a>.</p>
<h2>Basta Michè, hai fatto un prequel che è durato sei mesi. Vuoi spiegarci o no com'è lavorare da remoto?</h2>
<p>Lavorare da remoto è ... meglio.</p>
<p>il telelavoro (quanto è anni '90 sta parola?) nasce da una consapevolezza: è possibile lavorare da casa a progetti seri, veri, con la stessa qualità di un lavoro d'ufficio.</p>
<p>Ciò significa anche che si possono gestire gli orari diversamente (io ancora non riesco a crederci e sto lavorando 9-18, comunque), così come il posto di lavoro: dei remote workers che conosco, pochi lavorano davvero da casa e quasi tutti preferiscono incontrarsi da qualche parte (spazi di coworking) per poter avere intorno qualcuno con cui prendere un caffè.</p>
<p>Dal punto di vista <em>qualitativo</em>, ho accettato questo lavoro anche perchè stavolta <strong>non sono più un mero esecutore di idee altrui</strong>, bensì posso proporre e realizzare una mia idea. Inoltre <strong>lavorare da remoto mi ha permesso di iniziare una vita più serena</strong>, ad esempio facendo sport ed evitando 2 ore di autobus per raggiungere Napoli ogni giorno.</p>
<p>Dal punto di vista <em>quantitativo</em> <strong>non ho notato nessuna differenza in termini di "quanto" lavoro</strong>: io dico sempre che quando c'è da lavorare di più, lo faccio, purchè non sia uno "straordinario permanente". Mi è capitato di rimanere fino alle 19.30 incollato ad un problema, perchè se avessi staccato avrei perso il filo il giorno dopo. Ma l'ho fatto in totale autonomia e forse i miei capi non sanno nemmeno che è successo.</p>
<p>Da un punto di vista <em>organizzativo</em>, stiamo cercando di organizzarci e rendere la vita più facile a tutti, sia noi che lavoriamo in remoto sia coloro che seguono il nostro lavoro, quindi per ora stiamo usando:</p>
<ul>
<li>una sottoforma di Scrum, implementato via software tramite <strong>Jira</strong></li>
<li><strong>Slack</strong> è uno strumento irrinunciabile per gestire la comunicazione tra team, non solo per le chat, ma anche per sentirsi a voce e per, ehm, <em>cazzeggiare</em>.</li>
<li>**Github **e **Git **per il codice: che imbarazzo i primi tempi quando mi chiedevano di fare "pull sul branch", e io fino al giorno prima avevo lavorato con SVN.</li>
<li>la suite di <strong>Google Docs</strong> per email, documenti e file e qualche volta conferenze con hangouts.</li>
<li><strong>Spotify aziendale</strong>. Questo è uno dei benefit che gli altri mi invidiano di più, eppure costa 3€ al mese :p</li>
</ul>
<p>Per il resto, massima libertà di scelta dei tool (ad esempio io uso molto <strong>Visual Studio Code</strong>, mentre i colleghi usano <strong>WebStorm</strong>), della serie "se ti serve, usalo".</p>
<p>**La mia preoccupazione più grande, comunque, è di trovare il modo di riportare ai miei colleghi (e capi) quello che sto facendo, le decisioni che sto prendendo, etc etc. **</p>
<h2>Cosa serve per lavorare in remoto?</h2>
<ul>
<li><strong>Essere autonomi</strong>: se hai problemi devi essere bravo a risolverteli da solo. Sia chiaro, se chiedi aiuto a un collega è molto probabile che ti aiuterà (io stesso rispondo a chiunque mi chiede una mano) ma è difficile sistemare un problema se si è seduti a 800 km di distanza. Quindi, la capacità di ragionare e di risolversi i problemi da soli diventa fondamentale.</li>
<li>Sempre sull'autonomia, può capitare che per proseguire il lavoro serva la risposta di un collega, che in quel momento è impegnato in una riunione: <strong>piuttosto che stare con le mani in mano è opportuno portarsi avanti</strong>, magari ragionando sui prossimi task.</li>
<li><strong>Essere onesti e ragionevoli</strong>: quando si parla di stime non ha senso mentire, e lo stesso vale sui problemi che si potrebbero incontrare. Una cosa che in genere richiedono ai remote workers è di avere una forte personalità e di "<em>lottare per le proprie idee</em>".</li>
<li><strong>Ogni tanto è imporante vedersi</strong>: io cerco di incontrare gli altri "resident" del gruppo ogni 3-4 mesi, ma questo dipende un po' da ognuno.</li>
</ul>
<h2>Lo consiglieresti a chiunque?</h2>
<p><strong>No</strong>, penso che il lavoro remoto debba essere un po' una scelta e un po' una conquista.</p>
<p><strong>All'inizio della propria carriera è importante conoscere il mondo del lavoro tradizionale</strong>: avere dei colleghi, un capo, delle regole da rispettare (anche sul dressing code!) ti permette di capire se questo è il mondo che fa per te. Ne ho conosciute di persone che si lamentano, eppure l'azienda è la loro vita.</p>
<p>Inoltre, **un giovane ha bisogno di "mentori" che possano indirizzarlo **nelle scelte o comunque consigliarlo rispetto a dove sta andando il mercato. Per un telelavoratore tutto questo potrebbe non esserci.</p>
<p>E non dimentichiamo la solitudine: se ne soffri, telelavorare non è sicuramente per te.</p>
<h2>Le aziende tradizionali scompariranno? Lavoreremo tutti da casa?</h2>
<p>Non credo proprio, altrimenti non sarà più così figo lavorare da remoto 🙂</p>
Come ho cambiato lavoro (Prequel di: com'è lavorare da remoto?)
2016-09-19T09:14:50Z
https://michelenasti.com/2016/09/come-ho-cambiato-lavoro-prequel-di-come-lavorare-da-remoto/
<p>Vi ricordate che ho <a href="http://michelenasti.com/2016/06/cambio-lavoro-alla-scoperta-del-remote-working/">cambiato lavoro</a>? E'giunto il momento di parlarne un po'.</p>
<h2>Per chi lavori?</h2>
<p>La mia società si chiama <a href="http://www.bemind.me/">Bemind</a> e, a parte il fatto di sentirci molto fighi per il fatto di essere <em>Beminders</em>, per ora non siamo molto presenti sul web. E' una società molto giovane nata dall'idea di un CEO che un bel giorno ha detto: io non voglio fare il dipendente di nessuno. Se il CEO vuole palesarsi su questo blog, a disposizione!</p>
<h2>Come vi siete conosciuti?</h2>
<p>Questo è un argomento sottovalutato da molti, eppure secondo me è fondamentale. Circa un anno fa ho trovato un loro annuncio non sui soliti siti noiosi, ma su posti interessanti come <em>Stack Overflow</em> e <em>LinkedIn</em> (primo punto a loro favore).</p>
<p><strong>L'annuncio</strong> <strong>non era il solito listone di competenze da possedere</strong>, bensì spiegava per sommi capi che tipo di persona cercavano, quindi un "search by personality"; Mi ha colpito il fatto che <strong>non mi è stato richiesto di inviare un curriculum, bensì una lista dei tre libri tecnici più interessanti che avessi letto</strong>, oltre a una breve descrizione del perchè li avessi trovato interessanti. Poi è seguita una call in cui ci siamo effettivamente conosciuti.</p>
<p>Purtroppo in quella occasione cercavano una figura più "part time", visto che il progetto che stavano realizzando era ad alto rischio, e non avevano garanzie che prendesse piede (spoiler: ha preso piede). Così mi chiesero di risentirci più in là nel tempo.</p>
<p>Una persona normale avrebbe dimenticato l'episodio e sarebbe andato avanti, ma io ho avuto un presentimento molto positivo dopo quella call. Periodicamente li ho scritti chiedendo info dal punto di vista tecnico, specialmente sui microservizi (su cui ci siamo scambiati feedback e libri) e su altri eventi tecnici nazionali. Ad esempio, ho sentito parlare di un framework per microservizi chiamato <a href="https://github.com/spotify/apollo">Apollo</a> (di Spotify) e loro mi hanno risposto con un progetto con una filosofia molto simile, <a href="http://sparkjava.com/">Spark</a>.</p>
<p>Dopo circa 6 mesi in cui ci siamo sporadicamente scritti, principalmente scambiandoci opinioni su argomenti tecnici, ecco che vengo ricontattato dal mitico CEO che mi chiede se sono ancora disponibile a lavorare con loro.</p>
<h2>Cosa fai?</h2>
<p>Il mio ruolo principale è lavorare alla diffusione di <strong><a href="https://www.gestpay.it/gestpay/index.jsp">Gestpay</a> di Banca Sella</strong>, il gateway italiano per i pagamenti. Se avete un e-commerce, una startup, o rivendete servizi e avete bisogno di accettare pagamenti dai vostri utenti, Gestpay è LA soluzione, che stiamo completamente rinnovando. (Ne esistono anche altre: di sicuro conoscerete Paypal, e noi siamo loro partner nel senso che potete accettare pagamenti anche attraverso di loro, oltre che con le carte di credito, e ricevere i soldi nella stessa dashboard).</p>
<p>Nei prossimi giorni pubblicherò l'articolo che ho già scritto: <em><a href="http://michelenasti.com/2016/09/come-lavorare-da-remoto/">com'è lavorare da remoto</a>?</em></p>
Develop a microservice with Ratpack
2016-10-28T18:30:55Z
https://michelenasti.com/2016/10/develop-a-microservice-with-ratpack/
<p><a href="https://ratpack.io/">Ratpack</a> is a lightly opinionated framework that's perfect for backends written with a RESTful specification. I am using it in a project that we are developing, and of course it's a microservice.</p>
<p>Ratpack is completely <strong>written in Java 8</strong>, and you can use it with Java if you like. However, it's in <strong>Groovy</strong> that it really shines. Groovy is a functional scripting language for the JVM, and enhances Java under many aspects.</p>
<p>I was scared of using Groovy, a language that I have never studied systematically. But <strong>Groovy is not that difficult</strong>. In fact, I found it easier to read (and write) Groovy examples than the Java counterpart.</p>
<p>So, right now I'm stuck with the Java version of the framework, that is functionally equivalent to the groovy one. But I have already in mind to rewrite everything in groovy, once I have stabilized the requirements. <strong>Microservices are</strong> also this: <strong>the possibility to rewrite everything when you find a better option</strong>.</p>
<p>So, what's great about ratpack? what are it's main features? Let's dig in it.</p>
<h3>Ratpack Features</h3>
<p><strong>Ratpack</strong> <strong>does not force you</strong> to stuck with their idea <strong>on how structure your code</strong>.</p>
<p><strong>Ratapack has built in support for startup configuration</strong> coming from json, yaml, environment variables, system arguments, and java classes. This is great since many of us are going to use Ratpack in a container.</p>
<p>With ratpack you can specify the API of your backend in a fluent way, so that <strong>it's very easy to declare and to understand who will answer a call</strong>. Here's an example:</p>
<p><div class="gist-oembed" data-gist="musikele/0a32762eddc82d1de801e66029dcdb07.json">
</div></p>
<p>It has native <strong>support for headers, status codes, Exceptions, query parameters, forms, json</strong>, and all the like.</p>
<p>Guice is the default <strong>dependency injection</strong>, but you can go on with Spring if you want.</p>
<p>The class that responds to a http call is a <strong>Handler</strong>, that you have to imagine <strong>like a java servlet that intercepts a request</strong>. You can, for example, log all requests in a handler, then pass the control to the next that will extract data from headers, and according to that data it will call the right handler for you. You can also pass data between handlers so that it's very easy to work with it.</p>
<p>Want more? <strong>Ratpack is implemented using <a href="http://netty.io/">Netty</a></strong>, an asynchronous event-driven network application framework. Does this definition say anything to you? <strong>It's the same philosophy taken by NodeJS</strong>. So you can expect to handle a lot of connections. (This power comes at a cost. Develop responsibly)</p>
<p><strong>Testing is a breeze</strong>. I have done only functional tests, but I'm confident that the application works exactly like I am expecting. Ratpack encourages to use Spock, but I'm using JUnit and I'm not regretting it.</p>
<p>And finally, <strong>in development mode</strong> (launching it with "gradle -t run") <strong>it will auto-reload the server everytime there's a change on filesystem</strong>. If you have ever worked with tomcat, you know how much time this can save you.</p>
<h2>How to learn?</h2>
<p>Don't read the official documentation - it's just a waste of time. Instead, <strong>buy a copy of <a href="http://shop.oreilly.com/product/0636920037545.do">Learning Ratpack</a></strong> - this book is clear and covers every aspect of a Ratpack application. I'm still at chapter five but it's very easy to go through!</p>
<h2>Plans for the future</h2>
<p>I want to rewrite everything in Groovy, as I said. I also have to finish the book to uncover all Ratpack powers. I have made many mistakes in the past, expecially from not having undestood how handlers work and how to pass data between handlers. That's why I say the book is useful.</p>
<p>If you have ever tried ratpack, feel free to give me your impressions! Expecially if they are negative.</p>
OrientDB
2016-11-04T10:06:00Z
https://michelenasti.com/2016/11/orientdb/
<p>Convenite con me che fare un DB é la cosa più difficile che ci sia? Seconda solo a fare un sistema operativo, diciamo 😇</p>
<p>Stasera ho conosciuto uno degli sviluppatori di <a href="http://orientdb.com/"><strong>OrientDB</strong></a>, un DB multimodello (parolone!). In pratica i dati possono essere memorizzati come nodi di un grafo, e le relazioni sono gli archi.</p>
<p>La cosa più simpatica é che i dati possono essere interrogati con un linguaggio molto simile all'sql, e si può accedere ai dati in formato Json con la dot notation.</p>
<p>La cosa positiva, rispetto a tutti gli altri DB NoSql, é che questo almeno gestisce i vincoli d'integrità, di univocità, cosa che MongoDB invece non fa.</p>
<p>OrientDb ha altre feature come la gestione del cluster completamente automatica e quindi la scalabilità, la navigazione tra oggetti che non comporta costosissime join...</p>
<p>Una feature visuale simpatica é la possibilità di vedere i dati come un grafo, invece che come tabella,quindi le relazioni sono esplicitate benissimo.</p>
<p>Non vi resta che scaricarlo, estrarlo, e lanciare la console di amministrazione. Buona sperimentazione!</p>
I was not satisfied with Time Machine so I wrote my own Backup System
2016-11-05T10:15:45Z
https://michelenasti.com/2016/11/build-your-own-cross-platform-time-machine/
<p>When I bought my first Apple computer, 4 years ago, I had a big problem: my data would be backed up in an encrypted and proprietary format. This meant that I could not access to my data from a linux or windows computer. I'm not a fanboy, and I happily use every possible system on earth, so this is a huge issue for me. 4 years ago I decided to overcome this problem by starting the writing <strong>My Own Cross-Platform bash-based Time Machine</strong>!</p>
<h2>Design principles</h2>
<ul>
<li><strong>incremental backups -</strong> the first backup is a specular copy of your hard drive (well, in my case, of my home directory); the next backups will only transfer changed files.</li>
<li><strong>Space-efficient</strong> - using <a href="https://en.wikipedia.org/wiki/Hard_link">hard links</a>, I can do as many backups I want (every hour, every day...), non-changed files will be linked, not copied. No extra space is involved in this operation 😉</li>
<li><strong>Snapshot view /File Versioning</strong> - every directory is named after a timestamp, like "20161210-1359", so I know that this backup started at 13:59 the 10th of december of 2016.</li>
<li><strong>Work with my private NAS</strong> - I bought a NAS System by Synology, so my data is safely stored in my house.</li>
<li><strong>Work on different networks</strong> - When I'm far from home, I want to backup as well. This means I can backup from everywhere - as long there's a free internet connection 😉</li>
<li><strong>Possibility to be launched from every system</strong> - since it is a bash script, compatibility with Linux and Mac is assured. Some testing is required on Windows, but since Bash is now available on the platform, this should not be a big issue anymore.</li>
</ul>
<h2>Prerequisites</h2>
<ul>
<li>You should have <strong>your own server / NAS</strong>, and you can <strong>access it via SSH</strong>. I have the cheapest Synology, but it's perfect for my data because it has two mirrored disks. You may try with online systems like S3, as long you have ssh access, you can use it.</li>
<li>SSH access must be <a href="http://www.linuxproblem.org/art_9.html">configured to access without password</a>: to do this you must generate a pair of private/public keys and configure your server to accept your key. If you don't do this, the script will work but will ask the password 4 times.</li>
</ul>
<h2>How did I do this?</h2>
<ul>
<li>The magic is all by <strong><a href="http://rsync.samba.org/">rsync</a>:</strong> this software is the real engine that does the transfer, securely, efficiently.</li>
<li>the script is programmend in <strong>bash</strong>, the worst programming language ever. There is no debug, no IDE support, the strangest syntax, but it is also the software that powers the best available shell.</li>
</ul>
<h2>How does it work?</h2>
<ol>
<li>The script will try to connect to the server and check if the backup folder exists.</li>
<li>since every backup is stored in a directory named after date and time (example: <code>20161225-1400</code>), sort the directory and take the latest. This will be the base directory for calculating the difference.</li>
<li>create a new directory named after the current date and time, and <strong>hard link</strong> the files that are not changed on your computer.</li>
<li>copy only the modified files in the newly created directory.</li>
</ol>
<p>A hard link is a pointer to a file. If you create a hard link to a file, you can access the same file from two locations on filesystem. If you delete a hard link the other will continue to work. When you delete all the hard links, the file is lost forever (and the OS will mark that disk location as available for writing).</p>
<h2>What will you do now?</h2>
<ul>
<li>The project is very simple and <strong>open source</strong>! Feel free to understand how does it work.</li>
<li>I'm still developing a system to <strong>schedule backups</strong> on Mac. Next I'll move on other platforms. This means that now you have to launch the script by hand with <code>./start.sh</code> .</li>
<li>I also want to <strong>add some toast messages</strong> to say "hey! I'm going to backup everything. Click here if you don't want to" (for example, when you are under a mobile connection).</li>
<li>I also want to <strong>implement a solution that deletes unnecessary backups</strong>: it keeps 1 backup for every year, 1 backup for every month of the last 12 months, 1 backup per day for the last week, and one backup per hour for the last day.</li>
</ul>
<h2>WHERE IS THE SOURCE</h2>
<p>I was forgetting the most important part. You'll find the script on <a href="https://github.com/musikele/backupscript">github</a>. Cheers!</p>
Come riconoscere (e perché assumere) un buon "giocatore di squadra"
2016-11-21T09:30:00Z
https://michelenasti.com/2016/11/come-riconoscere-una-persona-che-fa-gioco-di-squadra/
<blockquote>
<p>Un buon "giocatore di squadra" ha queste qualità:</p>
<ul>
<li>Voglia di superarsi, fare più del dovuto per quell'extra che fa la differenza</li>
<li>La capacità di essere una persona che fa "gruppo", che sa stare con gli altri e sa comunicare</li>
<li>L'umiltà di mettere da parte l'ego per il bene del gruppo.</li>
</ul>
<p>Se manca una sola di queste tre qualità, il team intero ne risentirà.</p>
</blockquote>
<p>Questo é un sunto del libro "<a href="http://blnk.to/e7821b80">The Ideal Good Player</a>", ma potrei anche dire che é un sunto della mia esperienza lavorativa. Avendo lavorato con tante persone, sia più junior che senior di me, ho visto centinaia di casi umani da manuale:</p>
<ul>
<li>La persona che senza neanche presentarsi ti definisce bugiardo / incapace / you know it...</li>
<li>La persona che <em>pretende</em> il tuo aiuto</li>
<li>Colui che si prende il merito di una tua idea o, peggio, di una tua esecuzione</li>
<li>Quello con cui non ti trovi bene</li>
<li>Quello con cui tutto il team esibisce problemi relazionali (se é il capo, ahia! )</li>
<li>Quello che se ne frega di quello che fai, e come, l'importante é che lo fai in tempo</li>
<li>Colui che dice sempre che é colpa di qualcun altro</li>
<li>Quelli che nei meeting hanno sempre qualcosa da dire</li>
<li>.....!</li>
</ul>
<p>I colleghi con cui mi sono trovato meglio invece erano:</p>
<ul>
<li>Quelli con cui ti puoi fare una pausa caffé in tranquillità, anche per parlare di un problema di lavoro. (Esistono studi sul fatto che le pause caffé contribuiscono a risolvere problemi)</li>
<li>Quelli che sono davvero appassionati di ciò che fanno</li>
<li>Quelli che il lavoro é il loro hobby preferito</li>
<li>Quello a cui ho chiesto aiuto, anche alle 18, ed é rimasto con me a risolvere un disastro...</li>
</ul>
<p>Non siamo tutti uguali e non siamo sempre nella stessa categoria, anzi in base ai progetti, momenti, colleghi noi stessi cambiamo umore e atteggiamento. Io stesso sono stato un pessimo stronzo e un generoso altruista, con le stesse persone a distanza di poche settimane!</p>
<p>Un'altra verità é che <strong>rockstar vere e proprie nel mio settore non le ho conosciute</strong> (tradotto: non ci ho mai lavorato assieme). Sto parlando di persone capaci di programmare una libreria da soli, nel tempo libero.</p>
<p>Di sicuro esistono, perché il web é pieno di persone con le palle che sanno quel che dicono; ma in generale, la stragrande maggioranza é fatta di medi o medio-scarsi.</p>
<p>Siamo dunque <strong>condannati alla mediocrità</strong>?</p>
<p><strong>No!</strong></p>
<p>Ci sono studi scientifici che riescono a dimostrare come <strong>l'intelligenza del gruppo sia superiore di quella del fuoriclasse.</strong></p>
<p>Questo significa anche che, una volta formato il team giusto, avrete tra le mani un mostro capace di giocarsela con <em>Cristiano Ronaldo</em> (nelle ipotetiche olimpiadi della calcio-programmazione).</p>
<p>Ma... C'é sempre un ma. I dipendenti non vanno solo trovati, assunti e foraggiati con lavoro e soldi; bisogna anche <strong>coccolarli, farli crescere (competenze, responsabilità...), dargli uno scopo. Motivarli.</strong> Se lo saprete fare, potrete giocarvela alla pari con tutto il Portogallo e, perché no, vincere l'Europeo!</p>
Esportazioni e reddito
2016-11-22T00:31:39Z
https://michelenasti.com/2016/11/esportazioni-e-reddito/
<blockquote>
<p>Una maggiore apertura al mercato dovrebbe ridurre la disuguaglianza nei paesi poveri, favorendo l'aumento della domanda per prodotti ad alta intensità di lavoro poco qualificato, per esempio i tessili, in cui sono di solito specializzati questi paesi. Ciò dovrebbe contribuire a far salire il livello dei salari dei lavoratori non qualificati rispetto a quelli dei lavoratori qualificati o rispetto al profitto dei capitalisti.</p>
<p>Nei paesi ricchi, l'apertura al commercio produrrebbe l'effetto opposto. Poiché questi paesi tendono a esportare prodotti ad alta tecnologia, la loro produzione richiede personale qualificato, per esempio ingegneri e tecnici; in tal modo il salario di chi ha compiuto studi superiori cresce rispetto a chi ha solo un'astrazione di base e la disuguaglianza, di conseguenza, aumenta.</p>
</blockquote>
<p>_(da "Chi ha e chi non ha", Branko Milanovic) _</p>
Ognuno vota le tasse che vuole pagare
2016-11-23T09:30:00Z
https://michelenasti.com/2016/11/ognuno-vota-le-tasse-che-vuole-pagare/
<blockquote>
<p>Le persone (tutte: i ricchi, la classe media e i poveri) votano esprimendo una preferenza per il livello delle imposte che desiderano o ritengono giusto, considerando che i benefici maggiori della spesa pubblica, finanziato attraverso le imposte, andranno ai poveri.</p>
<p>Società caratterizzato da alti tassi di disuguaglianza tenderanno a votare per alti livelli di tassazione, perché la popolazione povera che beneficerebbe dei trasferimenti pagando poche imposte é largamente maggiore rispetto alla popolazione ricca o benestante.</p>
<p>Ora, una tassazione alta riduce gli incentivi a investire e a lavorare sodo, e ciò riduce il tasso di crescita dell'economia.</p>
</blockquote>
<p>Da "Chi ha e chi non ha", Branko Milanovic</p>
Tasse e disuguaglianza
2016-11-23T09:30:00Z
https://michelenasti.com/2016/11/tasse-e-disuguaglianza/
<blockquote>
<p>Nel periodo in cui la tassazione diretta è aumentata rispetto a quella indiretta, la disuguaglianza é diminuita; nel periodo in cui la tassazione diretta é diminuita rispetto a quella indiretta, ovvero negli ultimi 25 anni, la disuguaglianza è aumentata.</p>
</blockquote>
<p><em>(Studi di Thomas Piketty, da "chi ha e chi non ha", Branko Milanovic)</em></p>
Perché un programmatore dovrebbe avere un blog
2016-12-12T00:00:00Z
https://michelenasti.com/2016/12/12/perche-un-programmatore-dovrebbe-avere-un-blog.html
<p>E' molto probabile che se leggi questo blog sei uno sviluppatore e ti starai chiedendo se è il caso di iniziare a bloggare di informatica e programmazione. La mia risposta è senza dubbio <strong>si</strong>.</p>
<h2>Perchè avere un blog su informatica e programmazione</h2>
<p>Non per i soldi, o meglio non direttamente. Sarà molto difficile riuscire a fare soldi con un blog, devi avere tante visite - e in un blog precedente ho guadagnato qualcosina di tangibile (10€?) quando ho scritto un post che è stato toccato 12000 volte in un giorno. Esperienza purtroppo mai più ripetuta :-(</p>
<p>E allora perchè avere un blog? Beh, si tratta di <em>personal branding</em>. Non c'è bisogno di dimostrare di essere i migliori programmatori su scala globale, certe volte basta esserlo su scala "locale" e godere di un'ottima visibilità, che qualcosa intorno a te inizierà inevitabilmente a muoversi ;)</p>
<p>Il blog è uno strumento perfetto per il personal branding, e insieme a un CV ben fatto (e un ottmo profilo LinkedIn) formano un'accoppiata formidabile. Infatti <a href="https://michelenasti.com/2016/12/12/2016/01/i-colloqui-di-lavoro-iniziano-e-finiscono-su-facebook/">le aziende si informano su chi siete tramite i social</a> e se riuscite a portarli sul vostro blog avrete fatto bingo già prima del colloquio.</p>
<p>Col blog dimostrerete:</p>
<ul>
<li>le vostre competenze</li>
<li>di essere un appassionato vero</li>
<li>di essere una persona capace di scrivere in italiano/inglese correttamente (lo spero per voi!).</li>
</ul>
<p>Una delle prime cose che ho fatto è di mettere l'indirizzo del mio blog ovunque, a partire dal CV. Se volete torniamo sull'argomento personal branding, ma nel prossimo articolo parlerò di qual è la miglior piattaforma per il blog di un informatico.</p>
Le migliori piattaforme di blogging (per un developer) a confronto
2016-12-15T00:00:00Z
https://michelenasti.com/2016/12/15/le-piattaforme-di-blogging-a-confronto.html
<p>Ho iniziato la mia avventura circa 10 anni fa usando <strong>Wordpress</strong>, un'eccellente piattaforma di blogging così versatile da poter diventare qualsiasi cosa: portale, sito vetrina, piattaforma di ecommerce... e anche blog, ovviamente!</p>
<blockquote>
<p>Per avere una visione più chiara dell'argomento <em>blogging for developers</em>, leggete anche il mio articolo precedente: <a href="https://michelenasti.com/2016/12/12/perche-un-programmatore-dovrebbe-avere-un-blog.html">Perché un programmatore dovrebbe avere un blog</a></p>
</blockquote>
<p>Tuttavia Wordpress ha alcuni requisiti che durano da 10 anni, ossia PHP e MySql. Ciò significa che per avere un blog bisogna necessariamente comprare uno spazio di hosting da qualche parte (a dire la verità i prezzi sono anche molto bassi per piattaforme non proprio performanti). Il modello di richiesta/risposta utilizzato da worpdress (principalmente <em>Apache</em>) e il fatto che per ogni richiesta si vada sul db, l'hanno resa una delle piattaforme più lente in assoluto.</p>
<blockquote>
<p>Nota bene: esistono hosting ultra-ottimizzati per wordpress che costano anche 100$/mese, e sono potentissimi. Ma voi non avete questi soldi.</p>
</blockquote>
<p>Esistono altre piattaforme di blogging, cercherò di spiegare velocemente perchè secondo me non sono buone per un informatico:</p>
<ul>
<li><strong>Medium</strong> è la piattaforma cool del momento, ed è perfetta per chi <em>non</em> scrive codice. Il bello di Medium è che ti trova i lettori (consigliando il tuo articolo a chi legge cose simili) ed è perfettamente integrato con i social. A parte il titolo e il contenuto del testo, non si può personalizzare nulla. Only content. No ads.</li>
<li><strong>Linkedin Pulse</strong> è la piattaforma di blogging di linkedin, e gli articoli condivisi su questa piattaforma vengono condivisi con gli utenti del social network. La cosa positiva è che non si parla a una platea generalista visto che gli utenti di linkedin sono quasi tutti "professionals" quindi si ricevono anche ottimi feedback. La cosa negativa è che non si può personalizzare alcunché.</li>
<li><strong>Tumblr</strong> è un'altro social che in Italia non è famosissimo. Ho (purtroppo) visto dei blog tecnici su Tumblr che si mischiano con i blog dei quattordicenni in cerca di ansie e con, beh, il porno che su tumblr la fa da padrona.</li>
</ul>
<p>Queste tre piattaforme non si pagano, e in generale non dovete fare nulla (hosting, installazione, etc) se non scrivere. Sarete però limitati a ciò che la piattaforma permette di fare: non potrete mettere ads, non potrete modificare il layout se non per alcune cose, etc.</p>
<p>Quasi tutte le piattaforme di cui vi ho parlato permettono di integrare codice esternamente tramite <a href="https://gist.github.com/">Gist di Github</a>, uno strumento fantastico per condividere snippet di codice al volo.</p>
<h2>E quindi?</h2>
<p>La mia scelta è ricaduta su <a href="http://jekyllrb.com/"><strong>Jekyll</strong></a>, anzi per essere precisi sull'accoppiata <strong>Jekyll + Github</strong>. Queste tecnologie sono alla base del blog che vedete ora, e in un prossimo articolo vi spiegherò le infinite potenzialità di questa scelta.</p>
<blockquote>
<p>Update: l'articolo su <a href="https://michelenasti.com/2016/12/22/jekyll-e-github-in-pratica.html">Jekyll & Github</a> l'ho pubblicato. Check it out!</p>
</blockquote>
Le tecnologie che compongono un blog Jekyll
2016-12-18T00:00:00Z
https://michelenasti.com/2016/12/18/le-tecnologie-che-compongono-un-blog-jekyll.html
<p>Gli ingredienti segreti di questo magico calderone che é il mio blog sono: <strong>Git</strong>, <strong>github</strong>, <strong>jekyll</strong> (con tanto <strong>html</strong>, <strong>css</strong> & <strong>javascript</strong>), <strong>markdown</strong>. Vediamoli in dettaglio.</p>
<blockquote>
<p>Per sapere come mai mi sono orientato proprio su Jekyll come piattaforma di blogging, leggete il mio precedente articolo <a href="https://michelenasti.com/2016/12/15/le-piattaforme-di-blogging-a-confronto.html">Le migliori piattaforme di blogging (per un developer) a confronto</a>.</p>
</blockquote>
<h2>Git</h2>
<p><a href="http://rogerdudler.github.io/git-guide/index.it.html"><strong>Git</strong></a> é un tool di versionamento distribuito. Senza essere troppo formali, supponete di lavorare in 15 su un progetto e di non avere internet, né i computer connessi in rete. Per condividere il codice tra membri del progetto usate le penne usb. Questo scenario non è lontano nel tempo, durante i miei primi anni d'università era così che si collaborava ai progetti.</p>
<p>Che succede se due Developer modificano lo stesso file? Come ce ne accorgiamo?</p>
<p>Un tool di VCS (version control system) risolve proprio questo problema, ne esistono di diversi tipi (svn, che é centralizzato, Git che é distribuito). Su Git ci sono libri e siti web: una vera rivoluzione da quando é apparso. <strong>Da conoscere OBBLIGATORIAMENTE</strong>.</p>
<h2>Github</h2>
<p><a href="http://www.github.com/">GitHub</a> é la community di programmatori di questo decennio, insieme a <em>Stack Overflow</em>. Su github ci sono chili e chili di codice open source che le aziende commerciali prendono a piene mani per realizzare i loro prodotti commerciali. Forse non scriverete mai una libreria capace di attirare migliaia di Developers, ma conviene comunque imparare a usare <strong>git</strong> per scaricare codice altrui ;)</p>
<p>Tramite git é possibile quindi scaricare codice altrui da github, vederne la history, e anche contribuire a progetti esterni. La cosa interessante é che potete caricare anche i VOSTRI progetti online, gratuitamente e illimitatamente, purché i repository siano pubblici (per quelli privati si paga). Ora che lo sapete, non dovreste mai più scrivere codice senza salvarlo su github.</p>
<h2>Jekyll</h2>
<p><a href="https://jekyllrb.com/">Jekyll</a> é un generatore di siti statici scritto in Ruby (ma non é importante conoscere Ruby) nato principalmente per generare blog, ma sul web si trovano facilmente dei template per creare anche altri tipi di siti: io ad esempio ho realizzato un sito-documentazione per un progetto a cui lavoro.</p>
<p>Il fatto che il sito generato sia statico (html, js & css) significa che non servono server potenti o software complessi per servire le richieste: il sito così costruito é il più veloce possibile - nessun tempo perso per costruire la pagina (il sito viene "compilato" all'inizio), nessuna richiesta a un database, ma solo latenza di rete (che non potete eliminare comunque).</p>
<p>Vista la semplicità del sistema, Github ha lanciato <em>GitHub Pages</em>: pubblicando il vostro repository "sorgente", github compilerà per voi il repo e lo mostrerà in formato html. Su come funziona tutto il giro, aspettatevi un mio aggiornamento a riguardo. (GitHub ha anche altre modalità di pubblicazione, alcune senza la potenza di Jekyll: provatele).</p>
<h3>Html, css & javascript</h3>
<p>Visto che Jekyll genera un sito statico, occorre dare una user interface al vostro sito che sia gradevole: con Jekyll non siete limitati nel tipo di soluzioni che potete implementare, anzi tutto ciò che é frontend. Qui potrete sbizzarrirvi con le soluzioni più belle che potete, nessun limite, solo creatività.</p>
<p>(WordPress, con i temi, ti nasconde questa fase e rende anche "scomoda" la personalizzazione di un tema).</p>
<h2>Markdown</h2>
<p>Come scrivo gli articoli del mio blog? Jekyll é predisposto per molti sistemi, ma quello più usato é <strong>Markdown</strong>. Esso é un <em>quasi</em>-standard, visto che ne esistono diverse implelentazioni e ci sono piccole divergenze, ma non é qualcosa che noterete davvero, almeno finché non dovrete andare "oltre".</p>
<p>Di base, un documento scritto in markdown non é nient'altro che un file di testo (solitamente con estensione <code>.md</code>) che jekyll trasformerà in un <code>.html</code>. La sintassi di markdown é molto leggibile, e non dovremo scimunirci per annidare tag nel modo corretto. Inoltre Mardown ha la possibilità di annidare codice sia inline <code>così</code>, sia scrivendo blocchi di codice su più righe:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//ad esempio, questo é un commento</span><br /><span class="token keyword">var</span> ciao <span class="token operator">=</span> <span class="token string">'beccatevi questo!'</span> <span class="token punctuation">;</span></code></pre>
<p>Questo post é stato scritto in markdown da un telefonino!</p>
<h2>ultime considerazioni</h2>
<p>Passare a Jekyll può essere un processo lungo, per il numero di concetti da abbracciare, ma in verità tutto si impara nel giro di 2-3 giorni.</p>
<p>La cosa positiva (e non mi stancherò mai di ripetere) é che nel realizzare il vostro blog/sito dimostrerete di conoscere tutta questa catena di tecnologie: Git, github, jekyll, markdown, html, css, javascript. Che combo!</p>
Jekyll & Github in pratica
2016-12-22T00:00:00Z
https://michelenasti.com/2016/12/22/jekyll-e-github-in-pratica.html
<p>In questo articolo spiegheremo rapidamente come installare un blog jekyll su github.</p>
<p><img src="https://michelenasti.com/images/jekyll_logo.png" alt="" /></p>
<blockquote>
<p><strong>Nerd Alert</strong>: dovete saper usare il <strong>terminale</strong> e <strong>git</strong> ! Se volete, facciamo un tutorial anche su questi due argomenti.</p>
</blockquote>
<blockquote>
<p>Ho <a href="https://michelenasti.com/2016/12/18/le-tecnologie-che-compongono-un-blog-jekyll.html">parlato ampiamente</a> del perchè Jekyll sia una buona soluzione di blogging, adatta agli sviluppatori.</p>
</blockquote>
<h2>Inizializzare Github</h2>
<p>Per prima cosa, <a href="https://github.com/join?source=login">aprite un account su Github</a>. Una volta registrati avrete un indirizzo col vostro nome utente. Ad esempio, il mio account è <code>http://github.com/musikele</code>.</p>
<p>La funzione di github che vi permette di pubblicare siti statici si chiama <strong>Github Pages</strong>. Potete scegliere se attivare le github pages per:</p>
<ul>
<li><strong>un progetto</strong> - ad esempio, se avete un repository git chiamato <code>blog</code> all'indirizzo <code>http://github.com/musikele/blog</code>), potreste servire i file contenuti all'indirizzo <code>http://musikele.github.io/blog</code>.</li>
<li><strong>il vostro nome utente (o organizzazione)</strong> - se create un repository chiamato <code>musikele.github.io</code> (quindi <code>http://github.com/musikele/musikele.github.io</code>), i file html saranno serviti direttamente da <code>http://musikele.github.com</code>.</li>
</ul>
<p>Quale sia la vostra scelta dipende da voi; per un blog personale è meglio un sito che non contiene sottopath, mentre per documentare un progetto è meglio il primo sistema.</p>
<p>Qualunque scelta abbiate fatto, siete già pronti per servire i vostri file. Facciamo subito una prova. Per non ripetere gli esempi due volte immaginiamo di stare creando un blog e quindi facciamo gli esempi sul <em>secondo caso</em>.</p>
<ol>
<li>Create e scaricate il repository col vostro username in locale</li>
</ol>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">git</span> clone https://github.com/<span class="token operator"><</span>username<span class="token operator">></span>/<span class="token operator"><</span>username<span class="token operator">></span>.github.io.git<br />$ <span class="token builtin class-name">cd</span> <span class="token operator"><</span>username<span class="token operator">></span>.github.io </code></pre>
<ol>
<li>in questa directory potrete creare un simpatico file html e committarlo: sarà automaticamente servito :)</li>
</ol>
<pre class="language-bash"><code class="language-bash">$ <span class="token builtin class-name">echo</span> <span class="token operator"><</span>h<span class="token operator"><span class="token file-descriptor important">1</span>></span>Hello World<span class="token operator"><</span>/h<span class="token operator"><span class="token file-descriptor important">1</span>></span> <span class="token operator">>></span> index.html<br />$ <span class="token function">git</span> commit <span class="token parameter variable">-am</span> <span class="token string">"first commit"</span><br />$ <span class="token function">git</span> push </code></pre>
<ol>
<li>puntate il browser a <code>http://<username>.github.io</code> e gustatevi il risultato.</li>
</ol>
<blockquote>
<p>Se c'è qualche step non chiaro, o troppo "magico", non esitare a scriverlo nei commenti. Rispondo a tutto.</p>
</blockquote>
<h2>Ma questo non è Jekyll: installiamolo</h2>
<p>Per prima cosa <a href="https://www.ruby-lang.org/it/">installate Ruby</a>, un linguggio di programmazione utilizzatissimo in USA e pochissimo in EU: le istruzioni variano da sistema a sistema, e forse sarà questo il passo più complesso del tutorial.</p>
<p>Una volta installato ruby, dovrete installare jekyll. Questo passo sarà facile grazie a un comando (<code>gem</code>) incluso in ruby che permette di installare script:</p>
<pre class="language-bash"><code class="language-bash">$ gem <span class="token function">install</span> jekyll bundler</code></pre>
<blockquote>
<p><code>jekyll</code> sappiamo cos'è, <code>budler</code> è un tool consigliato per evitare che futuri aggiornamenti rompano tutto.</p>
</blockquote>
<p>Ora jekyll è installato sul vostro sistema come applicativo utilizzabile da console.</p>
<p>Assumendo di trovarci nella cartella del bog (<code><username>.github.io/</code>), cancelliamo tutti i file precedentemente creati e creiamo la struttura di jekyll:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">rm</span> ./* <span class="token comment"># cancelliamo ogni file precedentemente creato... </span><br />$ jekyll new <span class="token builtin class-name">.</span> <span class="token comment"># crea la struttura base di jekyll nella directory corrente </span></code></pre>
<p>Verifichiamo che funziona tutto, lanciamo jekyll:</p>
<pre class="language-bash"><code class="language-bash">$ bundle <span class="token builtin class-name">exec</span> jekyll serve <br /> <span class="token comment"># => Now browse to http://localhost:4000 </span><br /> <span class="token comment"># Ctrl+C per interrompere </span></code></pre>
<p>Questo è un buon momento per committare...</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">git</span> commit <span class="token parameter variable">-am</span> <span class="token string">"jekyll new ."</span><br />$ <span class="token function">git</span> push</code></pre>
<h2>Com'è fatto jekyll</h2>
<p>La directory structure di Jekyll la trovate <a href="https://jekyllrb.com/docs/structure/">qui</a>. La cosa importante da sapere è che <code>_site/</code> conterrà il vostro sito generato in html, quello che github compilerà e servirà via http.</p>
<p>Nella directory <code>_posts/</code> scriveremo i nostri post.</p>
<p>Non voglio ripetere altro, visto che nella <a href="https://jekyllrb.com/docs/home/">doc ufficiale</a> di Jekyll c'è praticamente tutto quello che dovreste sapere. Solo una cosa mi preme dirvi...</p>
<p>Se avete committato, il vostro blog è già visibile a <code>http://<username>.github.io</code> - provare per credere !</p>
Web management of a Jekyll blog with Forestry
2017-01-02T00:00:00Z
https://michelenasti.com/2017/01/02/web-management-of-a-jekyll-blog-with-forestry.html
<p>I have successfully moved all my blog from Wordpress to Jekyll (what you see is the proof of it!) but there was one thing I was really missing: a web console to easily manage the blog when I don't have a computer on hand.</p>
<p>Then I found <a href="http://forestry.io/">Forestry.io</a>, a cloud enviornment to manage your static site.</p>
<h2>How is my blog set up</h2>
<p>My blog is hosted on github and I use some custom plugins that are not allowed by github default jekyll integration. That's why I use Travis-CI to build my website at every commit - if the build goes well, the build is pushed to github again (master branch) and served to you.</p>
<p>I explain my setup not because it's cool (it is <em>very</em> cool) but because <strong>forestry.io</strong> covers my case too, so I guess that can cover every possibile case.</p>
<h2>How does Forestry.io works</h2>
<p>Forestry.io is a cloud web application that gives you back what you miss more with Jekyll: a backend management for your website/blog.</p>
<p>You simply authorize Forestry to access your github account, tell forestry your blog repo (and branch), and voilà! You have a good backend for your Jekyll blog.</p>
<p>Here's a screenshot of my backend while I am writing this article in a wysiwyg editor:</p>
<p><img src="https://michelenasti.com/images/Schermata%202017-01-02%20alle%2012.35.41.png" alt="" /></p>
<p>You get a</p>
<ul>
<li><strong>wysiwyg editor</strong> for posts and pages that is completely in-sync with the repository</li>
<li>a <strong>custom url</strong> to access the blog-management without going to forestry.io website everytime</li>
<li>support for <strong>parameters</strong> in front matter (with auto recognition!)</li>
<li>support for <strong>drafts</strong></li>
<li><strong>upload</strong> of images and files</li>
</ul>
<p>In practice, they set-up a custom route (<code>/admin</code> ? you can choose it) with a <code>.js</code> file that connects with their servers and does all the magic for you.</p>
<p>All of this for the price of <strong>free</strong> (for one user/blog). Isn't this just <em>fantastic</em>?</p>
<p>You can still manage your blog from your preferred editor, offline, on your pc; Forestry will not get angry for this.</p>
<p>I only have a <strong>feature request</strong>: a <strong>native mobile app</strong> (android please!).</p>
<p>Kudos to them :)</p>
Tip del giorno: stampare comandi di uno script Bash
2017-01-12T15:37:51Z
https://michelenasti.com/2017/01/12/stampare-comandi-di-uno-script-bash.html
<p>Se avete scritto uno script bash <em>complesso</em> e volete cosa accade step by step, una soluzione semplice è usare lanciare il comando così:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">bash</span> <span class="token parameter variable">-x</span> script.sh </code></pre>
<p>In questo modo vedrete tutti i comandi che vengono lanciati, e le variabili che vengono settate, etc etc.</p>
<p>Voci mi dicono che si può addirittura debuggare Bash come se fosse un linguaggio di programmazione normale, ma non ho ancora avuto modo di provarlo.</p>
<p>Fonte: <a href="https://stackoverflow.com/questions/5750450/bash-print-each-command-before-executing">StackOverflow</a> (dove se no?)</p>
PRIMO LAVORO DA INFORMATICO - meglio una piccola o una grande azienda?
2017-01-30T09:45:00Z
https://michelenasti.com/2017/01/30/primo-lavoro-da-informatico-meglio-una-piccola-o-una-grande-azienda.html
<p>Dopo aver partecipato a una bella discussione on line su quale fosse la migliore scelta per il <em>primo lavoro per un programmatore</em>,ho deciso di ripubblicare la mia risposta anche sul blog.</p>
<p><a href="https://www.facebook.com/groups/OrientamentoUscitaInformaticaSalerno/permalink/1220485904665717/">Su Facebook</a> troverete il thread originale.</p>
<p><strong>In questi giorni mi domando spesso se per una persona che deve affacciarsi al mondo del lavoro sarebbe meglio aspirare ad entrare in una grande azienda o è indifferente?</strong></p>
<p>_La mia riposta: _</p>
<p>Ho lavorato in due aziende medie e in una più piccola. Il primo assunto é fare ciò che ti piace, a quel punto starai bene ovunque.</p>
<p>Da un punto di vista qualitativo, le aziende grandi non preferiscono tecnologie cutting edge e anzi preferiscono la stabilità di tecnologie consolidate su cui hanno investito soldi e skill. I progetti che realizzano sono generalmente di system integration (... gestionali) con alti carichi di utenti e dati. In una grande azienda il lavoro può essere bello o brutto a seconda di com'é il capo, ma tieni presente che sei solo un numero. <em>Ovviamente ci sono tante eccezioni a quello che ti ho detto.</em></p>
<p>In una startup o piccola azienda cambia tutto. I progetti in genere o hanno il requisito di <strong>consegnare subito</strong> (se é un'azienda di consulenza) o di <strong>essere belli e fighi</strong> (se cerchi di vendere un prodotto tuo). Hai più controllo su ciò che puoi fare, puoi decidere come farlo, spesso lavorando con lo strumento che ritieni più giusto per il tuo lavoro. In termini di soldi puoi guadagnare più o meno rispetto ad un enterprise, soprattutto se l'azienda é tua. Di sicuro impari tanto perché dovrai capirne non solo di software ma metterai la mano a marketing, pubblicità, social, etc. Insomma ce n'é per tutti.<br />
<em>Anche qui ci sono le dovute eccezioni.</em></p>
<p>In ogni caso non considerarti mai arrivato: continua a fare colloqui con le aziende che reputi più interessanti, e migliora sempre il tuo "saldo mensile". Nessuno ti darà un aumento perché sei un dipendente "fedele"!</p>
<p>Mentre sono sul treno, di ritorno da una trasferta di lavoro, ho aggiunto un altro pezzo di ragionamento. <strong>Lavorando in una big corporation cosa si guadagna?</strong> A parte i soldi, intendo.</p>
<p>In una big si impara a <strong>lavorare con metodo</strong>, a fare <em>stime</em>. Si impara anche a fare <em>un mucchio di soldi</em> (non voi, il progetto!) su decisioni tecniche anche un po' obsolete.</p>
<p>In una piccola azienda invece si imparano gli strumenti più alla moda, si ha la possibilità di entrare a contatto con ambienti più vivaci, dove si seguono i trend. Forse <em>l'aspetto tecnico conta di più</em>.</p>
<p>Se non pensiamo per un attimo al primo ma al secondo, successivo lavoro, forse da un'azienda piccola é più facile saltare a un'azienda grande. Viceversa, da una grande azienda che ti da stabilità, ma ti insegna cose vecchie, bisogna fare qualche sforzo in più se si vuole andare verso aziende più piccole (o si vuole fondarne una).</p>
Tool of the week: Develop faster in NodeJS with Nodemon
2017-01-31T09:56:51Z
https://michelenasti.com/2017/01/31/develop-faster-in-nodejs-with-nodemon.html
<p>Are you frustrated when you hit an error in your NodeJS application because, after a crash, you have to restart it? Do you want to <em>immediately</em> see the app restarted when you change a file? With <strong>Nodemon</strong> this will never be a problem again.</p>
<p>When you start a NodeJS application, you usually do something like this:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">node</span> app.js </code></pre>
<p>but what if the app crashes? What if you change the content of a file? we must return to the terminal and start it again! What a waste of time!</p>
<p>Yes, I know that node applications start in a heartbeat. But... we still have to ALT-TAB to the console and restart :/</p>
<p>That's what <a href="https://github.com/remy/nodemon"><em>nodemon</em></a> is for. It will restart the app for you in case of crashes of file changes.</p>
<p>Simply install it globally</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> nodemon </code></pre>
<p>and start your applications with nodemon:</p>
<pre class="language-shell"><code class="language-shell">nodemon app.js </code></pre>
<p>You can also pass parameters via command line, if you need it; nodemon will handle everything for you.</p>
<p>Under the hood, Nodemon will check if your files have changed (or if the process is terminated) and will restart your app everytime the condition is met.</p>
<p>A very useful tool for developer ninjas!</p>
Node useful libraries - manage command line arguments with YARGS
2017-02-02T10:45:38Z
https://michelenasti.com/2017/02/02/node-useful-libraries-manage-command-line-arguments-with-yargs.html
<p>When dealing with command line arguments in NodeJS we all know that we have to enquire <code>process.argv</code> object.</p>
<p>This object contains all the strings that compose our nodejs app, so for example</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> bank.js balance </code></pre>
<p>can be read inside <code>bank.js</code> as:</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>argv<span class="token punctuation">)</span> <br /><span class="token comment">/* outputs: <br />[<br /> '/usr/local/bin/node', <br /> '/path/to/bank.js',<br /> 'balance'<br />]<br />*/</span></code></pre>
<p>That's a simple case, and we know that <code>process.argv[2]</code> will contain the "command" provided to our script, <code>bank.js</code> (that is also <code>process.argv[1]</code>).</p>
<p>But what if we want to accept parameters like:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> bank.js sendMoney <span class="token parameter variable">--recipient</span><span class="token operator">=</span><span class="token string">"James"</span> </code></pre>
<p>or this:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> bank.js sendMoney <span class="token parameter variable">--recipient</span> <span class="token string">"James Woods"</span> </code></pre>
<p>Well, now the problem is becoming a bit more complex, expecially if we want to handle everything with <code>process.argv</code>. Maybe if there's a library that handles all of this...</p>
<h2>YARGS: the module that handles command line parameters</h2>
<p><a href="http://yargs.js.org/">YARGS</a> is a npm module that will transform your <code>process.argv</code> into a javascript object. It will handle all the special cases, spaces, quotes, arguments, for you.</p>
<p>Yargs is completely configurable, but its defaults are the best choice for you and your users.</p>
<p>First, let's install this module and save it in our current project:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">npm</span> i <span class="token parameter variable">-s</span> yargs </code></pre>
<p>To use yargs you only have two steps to do:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> yargs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'yargs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> argv <span class="token operator">=</span> yargs<span class="token punctuation">.</span>argv<span class="token punctuation">;</span> </code></pre>
<p>Now we have an object <code>argv</code> with all the properties set to the command line.</p>
<p>Let's do a quick example:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> bank.js sendMoney <span class="token parameter variable">--recipient</span> <span class="token string">"James Woods"</span> </code></pre>
<p>process.argv will show this:</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>process<span class="token punctuation">.</span>argv<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token comment">/* prints: <br />[ '/usr/local/bin/node',<br /> '/path/to/bank.js',<br /> 'sendMoney',<br /> '--recipient',<br /> 'James Woods' ]<br />*/</span></code></pre>
<p>While YARGS will return this:</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>argv<span class="token punctuation">)</span><br /><span class="token comment">/* prints: <br />{ _: [ 'sendMoney' ],<br /> recipient: 'James Woods',<br /> '$0': 'bank.js' }<br />*/</span> </code></pre>
<p>As you can see, now we have a Javascript obect with some properties that resamble what we have passed on the command line.</p>
<ul>
<li><code>argv._</code> contains the program commands, like <code>balance</code>, <code>sendMoney</code>, etc.</li>
<li><code>argv.recipient</code> contains the property associated with <code>--recipient="James Woods"</code>. Note: you can also use spaces, or remove the quotes in case of a single word... Yargs will contain this for you.</li>
<li><code>argv.$0</code> contains the name of the js file, in case you need it.</li>
</ul>
<p>Easy, isn't it?</p>
<p>Now you are ready to write your beautiful command line app and be super productive !</p>
Quali sono gli svantaggi del lavoro da remoto?
2017-02-05T00:00:00Z
https://michelenasti.com/2017/02/05/quali-sono-gli-svantaggi-nel-lavorare-da-casa.html
<p>Salvo sporadiche trasferte a Biella, dove risiedono gran parte dei miei colleghi, io lavoro prevalentemente da remoto, a Salerno.</p>
<p>Quando lavoravo in un'azienda tradizionale pensavo al lavoro da remoto come la <em>liberazione da tutti i mali</em>; non ho cambiato idea, ma adesso sono più consapevole che <strong>ci sono anche dei lati negativi,</strong> che tra l'altro impattano poco sulla qualità del lavoro e molto sulla qualità della vita.</p>
<h2>1. Camminerai molto meno</h2>
<p>Grazie all'Activity Tracker che ho comprato da qualche anno possiamo fare un'analisi sui passi fatti quando lavoravo in azienda e da remoto.</p>
<p>Questa è una <strong>settimana tipo del lavoro in azienda</strong>. Le misurazioni risalgono a <strong>gennaio 2016</strong>:</p>
<p><img src="https://michelenasti.com/images/Schermata%202017-02-05%20alle%2019.42.56.png" alt="" /></p>
<p>Quella settimana <strong>ho fatto 78.000 passi</strong> - sono più di 10.000 passi al giorno. (Il sonno è sempre stato un problema, mi piace andare a letto tardi).</p>
<p>Una giornata di lavoro tipica era così composta:</p>
<ul>
<li><strong>6.50</strong>: sveglia</li>
<li><strong>7.25</strong>: autobus per Napoli</li>
<li><strong>8.15</strong>: arrivo a Napoli, se è bel tempo vado a piedi (circa 15-20 min).</li>
<li><strong>8.45</strong>: arrivo in ufficio ... inizio giornata di lavoro</li>
<li><strong>18.00</strong>: fine giornata di lavoro, a piedi verso l'autobus (15-20 minuti)</li>
</ul>
<p>2 volte a settimana c'è allenamento e il weekend poteva capitare una partita di tennis.</p>
<p>Passiamo ora allo stesso periodo (più o meno) del 2017, <strong>quando già lavoravo da remoto</strong>. Aggravante: c'è anche una neonata per casa, che a 2 mesi richiede molte attenzioni :)</p>
<p><img src="https://michelenasti.com/images/Schermata%202017-02-05%20alle%2019.43.16.png" alt="" /></p>
<p>Lasciamo stare la notte del venerdì, in cui ho probabilmente tolto l'orologio, per il resto il grafico è molto indicativo.</p>
<p><strong>Il numero di passi è sceso a 51000</strong>: 27000 passi in meno.</p>
<p>La mia giornata tipica è così cambiata:</p>
<ul>
<li>sveglia intorno alle <strong>8:00</strong>, colazione, etc.</li>
<li><strong>ore 9:00</strong>: mi sposto nell'ufficio di fianco casa, dove lavorano anche altre persone che però fanno altro.</li>
<li><strong>ore 18:30-19:00</strong>: finisco di lavorare e mi dedico a famiglia, hobby, sport, etc.</li>
</ul>
<p>Gli allenamenti di tennis sono rimasti invariati, sempre 2 a settimana, più una partita nel weekend.</p>
<p><strong>Morale:</strong> lavorando da remoto non solo potrete organizzare il lavoro come credete, ma anche la vostra vita. <strong>Cercate di non soccombere alle comodità</strong>: praticare sport non basta. Serve un'attitudine a fare di più.</p>
<p><strong>Soluzione</strong>: mi impegnerò a fare una <strong>corsetta</strong> / passeggiata la <strong>mattina</strong>. Chi viene con me?</p>
<h2>Scompare la cognizione del tempo, soprattutto se il problema è interessante</h2>
<p>Quando lavoravo in un'azienda tradizionale solo alcuni problemi erano interessanti, più spesso era il solito copia & incolla. Alle 17.50 mi buttavo giù per le scale felice di aver completato un'altra giornata di lavoro, per prendere l'autobus e tornare a casa.</p>
<p>Adesso invece mi piace trovare soluzioni sfiziose anche a problemi semplici, e in generale mi diverto a programmare "a modo mio" (nel miglior modo possibile, coi test automatici, code coverage, etc).</p>
<p><strong>La cognizione del tempo sfugge</strong>: non sono mai stato molto disciplinato, e sono anche una persona che si distrae facilmente; ciò non aiuta.</p>
<p>Così <strong>capita spesso di restare a programmare fino alle 19 e anche le 19.30</strong>, tant'è che rischio di fare tardi agli allenamenti e sentire mia moglie brontolare: "ma quando torni? non starai lavorando troppo?"</p>
<p><strong>Una volta pensavo che lavorando da remoto avrei eliminato tutte le distrazioni; anche se ho beneficiato di molti effetti positivi, mi sono ritrovato a lavorare di più.</strong></p>
<p><strong>Soluzione</strong>: anche qui, essendo un fattore direttamente dipendente da me, <strong>disciplina</strong>.</p>
<h2>Mancanza di colleghi con cui discutere a grandi linee di informatica</h2>
<p>E' bello internet, è bello facebook, è bello hacker news. E' bello twitter, slack, e il blog. Ma certe volte vorresti alzare la testa e sentire l'opinione di qualcuno che "ne capisce". Classiche domande che un collega può aiutare o comunque mostrare empatia:</p>
<ul>
<li>
<p>Devo comprare un computer nuovo, che prendo?</p>
</li>
<li>
<p>Perché venerdì compilava, e oggi (lunedì mattina) no?</p>
</li>
<li>
<p>Hai sentito l'ultima di Microsoft / Apple / Facebook / Google ... ?</p>
</li>
<li>
<p>Chissà se esiste una libreria che fa X-Y-Z (elenco di feature) ?</p>
</li>
</ul>
<p>Per non dare segni di schizofrenia preferisco lavorare da un ufficio vicino casa, dove ci sono altre fantastiche persone che si occupano di altro; sebbene si inizino le migliori discussioni generali, a volte sarebbe meglio chiacchierare con qualcuno che "parla la tua lingua".</p>
<p>Per questo motivo, quando posso, <strong>vado a lavorare a Salerno città</strong> dove altri amici informatici (anch'essi lavoratori da remoto) possono raggiungermi e lavorare insieme. <strong>Il consiglio, dunque, è di lavorare da un coworking e mai da casa</strong>.</p>
<p><em>Curiosità: andare a lavorare a Salerno costa (parcheggio e benzina) ma cammino anche 3-4000 passi in più.</em></p>
<h2>Conclusioni</h2>
<p>Resto dell'opinione che lavorare da remoto permette di migliorare la qualità del singolo e trovare i migliori talenti su scala più ampia; ma <strong>chi lavora da remoto ed è alla prima esperienza deve sapere che tra tante rose ci sono anche alcune spine.</strong></p>
Discesa in Inferno e risalita in Paradiso: le 24 ore da incubo di Gitlab
2017-02-07T11:33:49Z
https://michelenasti.com/2017/02/07/discesa-in-inferno-e-risalita-in-paradiso-le-24-ore-da-incubo-di-gitlab.html
<p><strong>Il 1 febbraio 2017</strong> è accaduto ciò che nessun'azienda al mondo si augura: un database admin, durante un'operazione di manutenzione di una macchina, lancia il comando sbagliato sul cluster sbagliato e cancella tutto il database di produzione, circa 300 gb di dati andati in fumo.</p>
<p><img src="https://michelenasti.com/images/gitlab_logo.svg" alt="" /></p>
<p><a href="https://about.gitlab.com/"><strong>Gitlab</strong></a> è una startup americana di hosting di codice via git. In questo settore le più note sono <strong>Github</strong> e <strong>Bitbucket</strong>, giusto per darvi un'idea dei competitor.</p>
<p>Gitlab era principalmente nota perché ospitava un'intera piattaforma di continuous integration al suo interno integrata con docker, cosa che gli altri competitor più affermati invece delegano a servizi esterni (a pagamento).</p>
<h3>Poco male, abbiamo i Backup... Abbiamo i backup, vero?</h3>
<p><strong>Gitlab va offline deliberatamente</strong>, finché non riescono a ripristinare il database. Pensate un attimo alla vostra azienda che chiude per un giorno o due a causa di un incidente di questo tipo: clienti paganti furiosi, il web che legge e scrive di te, migliaia di post indignati e social arrabbiati.</p>
<p>Contemporaneamente, <strong>una buona fetta di programmatori nel mondo inizia a provare compassione.</strong> Io stesso, autore di questo articolo, ho per errore cancellato una volta tutto il database di test (di test, per fortuna!) della mia azienda. Ma almeno avevamo un backup.</p>
<p>Torniamo a Gitlab: scatta la ricerca dei backup. Secondo le loro procedure interne, <strong>Gitlab ha almeno 5 backup differenti, ma uno dopo l'altro si scopre che non funzionano</strong>.</p>
<ul>
<li>
<p>I backup regolari sembrano non essere più in funzione: fallivano silenziosamente da mesi, e nessuno se ne era mai accorto.</p>
</li>
<li>
<p>Il dump del database non funziona a causa di un mismatch tra il tipo del db e l'eseguibile che lo leggeva/eseguiva.</p>
</li>
<li>
<p>gli snapshot del disco sono abilitati per i file comuni, ma non per i dischi del db.</p>
</li>
<li>
<p>Neanche il backup su S3 stava funzionando</p>
</li>
<li>
<p>a causa di alcuni script automatici che rimuovono i vecchi backup, non c'era più nulla di utilizzabile da poter ripristinare</p>
</li>
</ul>
<p>Fortunatamente, <strong>lo stesso admin che ha lanciato il comando per errore aveva fatto uno snapshot a mano del db sulla sua macchina locale.</strong></p>
<p>L'ultimo backup corrispondeva a 6 ore fa, e su una macchina non direttamente connessa al cluster di server; <strong>il ripristino del backup è avvenuto lentamente con una perdita di tutti i dati delle ultime sei ore.</strong></p>
<h3>Come trasformare un disastro in un'operazione mediatica</h3>
<p>E' a questo punto che in Gitlab si rendono conto di avere un'occasione da sfruttare: stanno ricevendo un'incredibile esposizione mediatica.</p>
<p><strong>La prima cosa che fanno è mettere su un <a href="https://docs.google.com/document/d/1GCK53YDcBWQveod9kfzW-VCxIABGiryG7_z_6jHdVik/pub">google docs</a></strong> condiviso in cui spiegano l'accaduto, i tentativi, le scoperte.</p>
<p>La seconda cosa che mi ha sorpreso è il <strong>live streaming su youtube</strong> (era <a href="https://www.youtube.com/watch?v=63wCG86ih94">qui</a>, ora non funziona più) dei dipendenti di gitlab che mostravano live tutte le operazioni messe in pratica per ripristinare il database. Tramite la chat i dipendenti di gitlab parlavano con gli spettatori, e rispondevano alle loro domande. A video c'erano le loro facce e il terminale che mostrava l'output di comandi impronunciabili.</p>
<p>La domanda più gettonata è stata questa:</p>
<blockquote>
<p>Q: Who did it, will they be fired?<br />
<strong>A: Someone made a mistake, they won't be fired.</strong></p>
</blockquote>
<p>(Traduzione: <em>Verrà licenziato quello che ha cancellato il db?</em> <strong>No, non sarà licenziato</strong>.)</p>
<h3>E ora?</h3>
<p><strong>Gitlab è di nuovo in funzione</strong>, il ripristino è stato lentamente completato e tutta questa vicenda ha generato un grande "<em>abbraccione</em>" da parte della community di sviluppatori di tutto il mondo.</p>
<p>Questo è quel genere di disastri che <strong>non dovrebbero MAI accadere</strong> e per cui <strong>conviene sempre avere più di un backup e testarlo regolarmente</strong>. E voi avete un piano di disaster recovery?</p>
Debug a NodeJS application from command line - it's super easy!
2017-02-08T12:17:15Z
https://michelenasti.com/2017/02/08/debug-a-nodejs-application-from-command-line-it-s-super-easy.html
<p>If you want to seriously develop a nodejs application, you can't continue to put <code>console.log()</code> statements everywhere. It's just a pain! That's why every modern language has debug support via various tools.</p>
<p>In NodeJS debugging can be also done via command line - it's not <em>point n' click</em>, but it's still easy and straightforward.</p>
<p>I want tell lies to you, <em>I debug my applications in a IDE</em> :) But if I'm debugging something over <code>ssh</code>, this tool comes in hand.</p>
<h2>An example file to play with</h2>
<p>Save this file as <code>book.js</code> :</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> book <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">'NodeJS for dummies'</span><span class="token punctuation">,</span><br /> <span class="token constant">ISBN</span><span class="token operator">:</span> <span class="token string">'1234567'</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br />book<span class="token punctuation">.</span>title <span class="token operator">=</span> <span class="token string">'cooking with Trump'</span><span class="token punctuation">;</span><br />book<span class="token punctuation">.</span>category <span class="token operator">=</span> <span class="token string">'recipes'</span><span class="token punctuation">;</span><br /><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>book<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>debug!</h2>
<p>To run this file you can launch <code>node book.js</code>; however, in case you want to <strong>debug</strong> you just add an option:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> debug book.js <br /><br /><span class="token operator"><</span> Debugger listening on <span class="token number">127.0</span>.0.1:5858<br />connecting to <span class="token number">127.0</span>.0.1:5858 <span class="token punctuation">..</span>. ok<br /><span class="token builtin class-name">break</span> <span class="token keyword">in</span> book.js:1<br /><span class="token operator">></span> <span class="token number">1</span> <span class="token builtin class-name">let</span> book <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token number">2</span> title: <span class="token string">'NodeJS for dummies'</span>,<br /> <span class="token number">3</span> ISBN: <span class="token string">'1234567'</span><br />debug<span class="token operator">></span></code></pre>
<p>Let's skip the first two lines, they are just diplayed for nerds.</p>
<p>Nodejs has blocked the execution at line 1 of the file, as you can see from the <code>></code> mark.</p>
<blockquote>
<p>It's best to say now that when NodeJS blocks at a line, the line is <strong>NOT</strong> executed. So, in the example, we are <em>before</em> the first istruction is ever executed.</p>
</blockquote>
<p>To continue to the next line, press <code>n</code>(next) and hit Enter:</p>
<pre class="language-bash"><code class="language-bash">debug<span class="token operator">></span> n<br /><span class="token builtin class-name">break</span> <span class="token keyword">in</span> book.js:6<br /> <span class="token number">4</span> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token number">5</span><br /><span class="token operator">></span> <span class="token number">6</span> book.title <span class="token operator">=</span> <span class="token string">'cooking with Trump'</span><span class="token punctuation">;</span><br /> <span class="token number">7</span> book.category <span class="token operator">=</span> <span class="token string">'recipes'</span><br /> <span class="token number">8</span></code></pre>
<p>Now the first line has been executed (the creation of the <code>book</code> object); node has now stopped to the next executable instruction.</p>
<p>In this easy example, going from one instruction to another using <code>n</code> might be interesting, but complex applications can contain thousands of lines of code, so jumping with <code>n</code> is not a great idea. Exit from debug for now.</p>
<blockquote>
<p>To exit from the debugger you can type <code>quit</code> or press <code>Ctrl+C</code> twice.</p>
</blockquote>
<p>We might want to stop at a specific point, for example before setting the <code>category</code> attribute to the book:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">...</span><br />book<span class="token punctuation">.</span>title <span class="token operator">=</span> <span class="token string">'cooking with Trump'</span><span class="token punctuation">;</span><br /><span class="token keyword">debugger</span><span class="token punctuation">;</span> <br />book<span class="token punctuation">.</span>category <span class="token operator">=</span> <span class="token string">'recipes'</span><span class="token punctuation">;</span><br /><span class="token operator">...</span></code></pre>
<p>if we want to jump directly to the place where the <code>debugger</code> instruction is, you can just hit <code>c</code> (continue):</p>
<pre class="language-bash"><code class="language-bash">debug<span class="token operator">></span> c<br /><span class="token builtin class-name">break</span> <span class="token keyword">in</span> book.js:7<br /> <span class="token number">5</span><br /> <span class="token number">6</span> book.title <span class="token operator">=</span> <span class="token string">'cooking with Trump'</span><br /><span class="token operator">></span> <span class="token number">7</span> debugger<span class="token punctuation">;</span><br /> <span class="token number">8</span> book.category <span class="token operator">=</span> <span class="token string">'recipes'</span><br /> <span class="token number">9</span><br />debug<span class="token operator">></span></code></pre>
<p>As you can see the debugger has now stopped at line 7, where the <code>debugger</code> instruction was. <strong>It's important to say that <code>debugger</code> instruction is ignored when running the application normally</strong>.</p>
<p>Ok, now we know how to stop in a nodejs application, but how can we inspect the value of objects?</p>
<p>just hit <code>repl</code>:</p>
<pre class="language-bash"><code class="language-bash">debug<span class="token operator">></span> repl<br />Press Ctrl + C to leave debug repl<br /><span class="token operator">></span></code></pre>
<p>Now we are in a Javascript console, like the one we have in a browser. we can execute expressions like <code>var a = 1+3</code>; the interesting thing is that we have <strong>access to variables defined until the break point</strong>: infact we can inspect our <code>book</code> variable and see that ...</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> book<br /><span class="token punctuation">{</span> title: <span class="token string">'cooking with Trump'</span>, ISBN: <span class="token string">'1234567'</span> <span class="token punctuation">}</span></code></pre>
<p>...<code>category</code> attribute is not defined, since it will be executed later, at line 8.</p>
<p>Using <code>repl</code> you can also modify the value of objects, and this is great if you are in a loop and want to go directly to the value that you want, or to simulate complex cases.</p>
<p>To exit from <code>repl</code> just hit <code>Ctrl+C</code> to return to <code>debug></code> prompt.</p>
<h2>Now you can debug like a boss</h2>
<p>Show this to your friends, they'll respect you more after this.</p>
hey javascript function, take this!
2017-02-10T10:05:22Z
https://michelenasti.com/2017/02/10/hey-javascript-function-take-this.html
<p>In this article I'm going to do a quick recap of ES5 and ES6 functions, explaining what happens to <code>this</code>.</p>
<p><img src="https://michelenasti.com/images/this.jpg" alt="" />\</p>
<h2>Functions as you may already know: plain-old-classic ES5</h2>
<p>As you know, in javascript you can define a function in many ways. The standard, 20-years-old way of declaring a function is this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"hello!"</span><span class="token punctuation">)</span> <br /><span class="token punctuation">}</span></code></pre>
<p>This kind of functions will be "hoisted", this means that the function will be read before any other code, and used when called. So you can also write this with no error:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// no error: this is executed later</span><br /><span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br /><span class="token comment">//this is read and hoisted first </span><br /><span class="token keyword">function</span> <span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"hello!"</span><span class="token punctuation">)</span> <br /><span class="token punctuation">}</span></code></pre>
<p>If you find this behaviour too strange (at first glance, it is!), you can <strong>assign a function to a variable</strong>. In this case the function will be read and executed only when used:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//sayHello(); if called here, like before, ERROR! </span><br /><br /><span class="token keyword">var</span> <span class="token function-variable function">sayHello</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Hello!"</span><span class="token punctuation">)</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// no error because it is used after the definition</span></code></pre>
<h2>Functions in ES6</h2>
<p>Now we can have a quick look to new kind of functions introduced in ES6. Here is the first example:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> <span class="token function-variable function">sayHello</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Hello, "</span> <span class="token operator">+</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token string">'Michele'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>what happened? we have stripped out the <code>function</code> keyword, put an arrow (<code>=></code>) between the arguments and the body of the function, and that's it! More expressive and fun to use.</p>
<h2>Functions, in ES6, in JS Objects</h2>
<p>There is just one thing to note: <strong>the ES6 version of the function does not bind <code>this</code></strong>. What the heck does this mean?!</p>
<p>Have a look at this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> user <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Michele'</span><span class="token punctuation">,</span><br /> <span class="token function-variable function">sayHi</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hi. I'm </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">sayHiAlt</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hi. I'm </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br />user<span class="token punctuation">.</span><span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "Hi. I'm undefined"</span><br />user<span class="token punctuation">.</span><span class="token function">sayHiAlt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "Hi. I'm Michele"</span></code></pre>
<p>What's happening? Let's dip in.</p>
<h3>what is <code>Hi. I'm ${this.name}</code>?</h3>
<p>This is a template string in Javascript. this translates in <code>"Hi. I\'m " + this.name</code>.</p>
<h3>Why the first <code>sayHi</code> returns undefined?</h3>
<p>because <code>this</code> is not bound to the context!</p>
<h3>What is happening at the last function, <code>sayHiAlt</code> ?!</h3>
<p>This is ES6 at all of it's power. First, we are assigning a function to a variable, like we saw in the third example of this article; then, we are using another ES6 to assign the function to a property with the same name.</p>
<p>Thanks to <a href="https://babeljs.io/repl/">babel online REPL</a> (a place where you can past ES6 code and see it in ES5), the previous snippet becomes this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token string">'use strict'</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">var</span> user <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Michele'</span><span class="token punctuation">,</span><br /> <span class="token function-variable function">sayHi</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hi. I\'m '</span> <span class="token operator">+</span> <span class="token keyword">undefined</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function-variable function">sayHiAlt</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token function">sayHiAlt</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hi. I\'m '</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<h2>What should I use?</h2>
<p>Use <code>() => {}</code> always, because it's simpler; usually you don't need <code>this</code> to work.</p>
<p>But if you need <code>this</code>, ... you know what to do.</p>
<p>After writing <code>this</code> article, I can finally say: ES6 doesn't care about <code>this</code>, but you should.</p>
I libri motivazionali funzionano?
2017-02-15T01:00:00Z
https://michelenasti.com/libri-motivazionali/
<p>In una discussione con alcuni miei amici abbiamo iniziato a parlare di libri che stavamo leggendo in questo periodo, e ne è nato un discorso sui libri <em>motivazionali</em> (quei libri che ti insegnano a essere una persona migliore).</p>
<p><strong>Enrico</strong> sta leggendo <a href="http://amzn.to/2mKm1Ti"><strong>Miracle Morning</strong>, <em>trasforma la tua vita un mattino alla volta prima delle 8:00</em></a>. E' uno dei libri che ho nella lista quindi sono estremamente curioso.</p>
<p><img src="https://michelenasti.com/images/miracle-morning.jpg" alt="miracle morning" /></p>
<p>Tuttavia, io ho una "politica" per cui non compro un nuovo libro se prima non ho finito il precedente, e sto ancora leggendo <a href="http://amzn.to/2mK757I"><strong>Chi ha e chi non ha</strong>, <em>Storie di disuguaglianze</em></a> libro a tratti pesantuccio e a tratti molto leggero che parla di disuguaglianza economica (non sociale).</p>
<p><img src="https://michelenasti.com/images/chi-ha-chi-non-ha.jpg" alt="Chi ha e chi non ha" /></p>
<p>Tra i capitoli si parla, ad esempio, di:</p>
<ul>
<li>come si fa a misurare se una nazione è composta da ricchi o poveri;</li>
<li>chi è stato l'uomo più ricco della storia;</li>
<li>come interpretare le migrazioni di massa dei nostri giorni;</li>
<li>perchè solo 8 club di calcio dopo il 2001 sono diventati ricchissimi e gli altri annaspano;</li>
<li>Stati Uniti e Argentina sono partiti dalla stessa condizione economica, nel 1800. come mai ora sono così diverse? cosa è successo?</li>
</ul>
<p>Da qui in poi il mio articolo assume un tono più colloquiale, anche per rappresentare meglio le cose che ci siamo detti.</p>
<p><strong>Michele (non io)</strong> - ho una domanda per voi. Leggendo tanto e provando a correggere alcune abitudini, non vi capita di non riuscirci? Ovvero di riuscirci solo all'inizio quando ci s'impone di cambiare qualcosa, ma poi appena cambia una virgola nella routine le cose lette vengono abbandonate. La domanda è quante volte le metodologie lette poi le avete davvero applicate nella vita quotidiana.</p>
<p><strong>Io</strong> - A me questo capita con lo sport... specialmente quello che faccio da solo. Avevo preso la bellissima abitudine di andare a correre alle 19 tutte le sere ... un'oretta di corsa. Poi una volta è venuto a piovere, un'altro giorno c'era un servizio da fare, poi un'altra cosa... e alla fine mi sono demotivato.</p>
<p><strong>Enrico</strong> - In effetti a ripensarci forse non ci sono mai riuscito semplicemente leggendo, anzi non ricordo proprio di aver mai ottenuto risultato. Di sicuro mi è capitato di cambiare abitudini nella mia vita ma ci sono riuscito quasi inconsciamente, perché ero fortemente motivato a raggiungere un obiettivo. L'ho fatto senza pianificare niente, semplicemente sono cambiato in base ad un desiderio e ad un'esigenza, quindi in maniera naturale. La cosa complicata, dove i libri possono aiutarti, è pianificare e forzare il cambiamento, ossia possono ispirarti in qualche modo e rafforzare qualcosa che stavi già pensando.</p>
<p><strong>Io</strong> - Un libro che è riuscito a cambiare qualcosa è stato <a href="http://amzn.to/2mJYCkP"><strong>è facile smettere di fumare se sai come farlo</strong></a>: avevo provato a smettere di fumare altre volte prima, ad esempio diminuendo le sigarette o smettendo di punto in bianco, e poi sono sempre tornato a fumare. Con il libro invece ho fumato fino all'ultima pagina, l'ho finito e mi sono chiesto: "mah?!" dopodiché non ho mai più fumato.</p>
<p><img src="https://michelenasti.com/images/smettere-fumare.jpg" alt="E' facile smettere di fumare se sai come farlo" /></p>
<blockquote>
<p>Ho fumato per un periodo circa 10 anni fa. Troppo tardi per arrabbiarsi ora, mamma :)</p>
</blockquote>
<p><strong>Michele</strong> - Waaa sta cosa di quel libro mi ha sempre intrigato, perchè non sei il primo che me lo dice. E' incredibile.</p>
<p><strong>Io</strong> - In quel caso penso che mi abbia dato delle motivazioni psicologiche profonde che mi hanno fatto decidere di non prendere mai più una sigaretta. Quasi a dire "sono un cretino se fumo ancora". Ma ammetto che ero fortemente determinato a smettere già di mio.
Altri amici a cui ho passato il libro e che non erano davvero intenzionati a smettere mi hanno detto tutti la stessa cosa: arrivati a metà libro si sono accorti che se avessero continuato avrebbero smesso davvero, ... e hanno smesso di leggere.
In generale comunque non vado molto dietro a questi libri motivazionali. Ho sempre paura che raccontino fuffa. Però se anche per 1 settimana mi fanno diventare una persona migliore, un tentativo bisogna farlo.</p>
<p><strong>Enrico</strong> - Ad esempio, avevo iniziato a svegliarmi prima perché mi rendevo conto la mattina passava troppo velocemente e non riuscivo a fare tutte le cose che volevo. Poi sono capitato su questo libro che ha confermato alcune intuizioni che avevo, mi ha spiegato anche un possibile metodo da adottare per poter ottenere di più. Ora lo adatterò un po' alle mie esigenze, però diciamo che ti dà una possibile strada da percorrere e ti suggerisce alcune riflessioni alle quali può darsi saresti arrivato da solo, ma anche no.</p>
<p><strong>Michele</strong> - E' logico che se è un argomento che già stai approfondendo, il risultato viene massimizzato. Quindi il concetto è tutto lì, questi libri aiutano quando sei pronto a recepirne l'insegnamento o le idee che ti propongono.</p>
<p><strong>Enrico</strong> - Si, sono strapieni di fuffa, nel ho letti abbastanza da capire come prendermi solo i punti salienti. Questi libri sono fatti per abbracciare un pubblico ampio e persuadere le menti di persone che vanno dalla casalinga di 60 anni al ragazzino di 16 anni.
E' normale che ti devono indorare la pillola in mille modi, ripeterti i concetti in mille salse fino a convincerti che quello che dicono è giusto; anche in questo, ad esempio, alcuni capitoli sono proprio l'identica copia di un altro, riformulato con esempi o strutturando il pensiero in ordine inverso. Alla fine si leggono velocemente proprio per questo :)
Vi posso dare un'altra informazione: il libro più bello che ho letto è <a href="https://michelenasti.com/2016/03/5-motivi-cui-rework-ti-cambiera-la-vita/">REWORK</a></p>
<p><img src="https://michelenasti.com/uploads/2016/03/wp-1457508741600-2.jpg" alt="Rework" /></p>
<ul>
<li>
<p><strong>IO</strong> - letto anch'io!</p>
</li>
<li>
<p><strong>Enrico</strong> - l'ho apprezzato oltre per quello che dice anche perché è scritto in maniera essenziale, va dritto al punto senza perdersi in mille chiacchiere.</p>
</li>
</ul>
<hr />
<p>Qui si conclude la discussione "aulica", poi da buoni amici abbiamo iniziato a parlare d'altro.</p>
<p>Mi piacerebbe sapere da voi, c'è qualche libro motivazionale che avete letto? Che giudizio ne date? Fatemi sapere la vostra nei commenti. Buon lavoro!</p>
The Javascript Event Loop for dummies
2017-02-25T11:29:00Z
https://michelenasti.com/the-javascript-event-loop-for-dummies/
<p>What does it mean that Javascript has no threads?! What is the event loop and how it is related? How can JS be even considered a modern programming language?! Let's find out the surprising truth about this stuff.</p>
<p><img src="https://michelenasti.com/images/o_weighted_hula_hoop-1.jpg" alt="An event loop. The dancer is not included." /></p>
<h2>Quiz: what's going on?</h2>
<p>Let's consider this small javascript program. What will be printed?</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Starting app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">//first block </span><br /><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'First setTimeout'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">//second block </span><br /><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Second setTimeout'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Finishing app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>In case you don't know: <code>setTimeout</code> is a javascript global function used to fire an action (the first argument) when the delay (second argument) is elapsed.</p>
<p>So the first <code>setTimeout()</code> block will wait two seconds and then will write <code>First setTimeout</code> on console.</p>
<p>The second block will wait 0 milliseconds (... it doesn't wait at all!) and then write <code>Second setTimeout</code> to the console).</p>
<p>Back to the quiz... what is the expected outcome?</p>
<p><table></p>
<p><thead></p>
<p><tr></p>
<p><th>#1</th></p>
<p><th>#2</th></p>
<p><th>#3</th></p>
<p><th>#4</th></p>
<p></tr></p>
<p></thead></p>
<p><tbody></p>
<p><tr></p>
<p><td>Starting App<br />
First setTimeout<br />
Second setTimeout<br />
Finishing app</td></p>
<p><td>Starting App<br />
Second setTimeout<br />
Finishing app<br />
First setTimeout</td></p>
<p><td>Starting App<br />
Finishing app<br />
Second setTimeout<br />
First setTimeout</td></p>
<p><td>Starting App<br />
Second setTimeout<br />
First setTimeout<br />
Finishing app</td></p>
<p></tr></p>
<p></tbody></p>
<p></table></p>
<p>What's your choice? Don't scroll down and think a little bit...</p>
<p>Hey, you could just paste this code in your browser's console to discover that ... the right answer is <strong>#3</strong>.</p>
<p>This behaviour may seem surprising but is perfectly legit.</p>
<p>Why?</p>
<h2>what is really happening under the hood</h2>
<p>Let's start with a Dogma: <strong>in Javascript there is just one thread</strong>. (<em>This is not correct anymore, because in the last years a new technology called <code>Service Workers</code> has become available; but it is not important for now.</em>) Everything is always executed in the same thread, even events that happen asynchronously.</p>
<p>So what happens when an asynchronous event happen, like the <code>setTimeout</code>?</p>
<p><code>setTimeout</code> is a good example of an asynchronous event. Expanding the reasoning, this can be a reading of a file, or receiving a packet over the network. The philosophy doesn't change.</p>
<p>Bearing this in mind, let's return to the basis.</p>
<ul>
<li>
<p>The first <code>console.log()</code> is printed: <code>Starting app</code></p>
</li>
<li>
<p>The first block is evaluated:</p>
</li>
</ul>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//first block </span><br /><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'First setTimeout'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This code says: <em>after 2000 milliseconds, trigger the function specified as first argument</em>. Since <code>() => { ... }</code> is a function, this is put in a <strong>callback queue</strong> ready to get fired when the conditions occur.</p>
<ul>
<li>The second block is evaluated:</li>
</ul>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//second block </span><br /><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Second setTimeout'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Another explanation. This code says: <em>after zero milliseconds, fire the function specified as first argument</em>. One would expect to be fired instantly, but this does not happen: The function is put in a <strong>callback queue</strong>, and when NodeJS believes the conditions are respected, the function is fired.</p>
<p>Node cannot fire the function NOW because there's other javascript code being executed in the stack: it's the <em>main program</em>, the one that starts with the first <code>console.log()</code>. Think of this as a being wrapped inside a function; nodeJS is evaluating <strong>this</strong> function and cannot evaluate others.</p>
<ul>
<li>
<p>The last <code>console.log()</code> is evaluated and <code>Finishing app</code> is written to console.</p>
</li>
<li>
<p>NodeJS controls the call queue and decides what to fire. There are two functions in the call queue. It's NodeJS's responsibility to select the right one: since the second <code>setTimeout</code> had a higher priority, it will be fired first: you'll then see <code>Second setTimeout</code>.</p>
</li>
<li>
<p>Node will pop the previous function from the stack and will check again the callback queue. This time it will select the first block <code>setTimeout</code> callback, and will print <code>First setTimeout</code>. Then, Node will pop this last function from the stack, and when it realizes that nothing more can be executed, the program will end.</p>
</li>
</ul>
<h3>What happens when you read data from a file or receive a response from the network?</h3>
<p>The behavior is the same! Node will put your callback function in the callback queue, and it will be called as soon as possible.</p>
<h3>The advantages of this</h3>
<p>The main advantage is that <strong>the CPU will not stop when waiting for an I/O event</strong>: it will just put the callback in the queue, and Node will execute the next available function. This means that one single CPU can handle more traffic and a greater number of concurrent connections; it is not limited by the number of threads a server can handle (because there is just one thread).</p>
<h3>The disadvantage</h3>
<p>The first disadvantage is that <strong>programming with async in mind and with callbacks is profoundly different</strong> from the past: it is more difficult to reason about and to debug, at start. However, once you truly understand the philosophy and the way it works, you'll never want to come back again.</p>
<h2>So? What you suggest?</h2>
<p>Node.js is a great idea made simple: a complex use case solved smartly. <strong>Learning Node will help you become a better programmer</strong>, and this applies even if you work with other languages.</p>
<p>I can only suggest you to google *NodeJS vs XXX performance", change XXX with whatever you want... You'll find that <a href="https://dzone.com/articles/performance-comparison-between">an interpreted language, with no tweaks, is faster than well-established platforms</a> (Caution: old article! but the main points still hold) with years of optimization.</p>
Quando l'azienda non paga gli stipendi
2017-02-28T10:36:03Z
https://michelenasti.com/2017/02/28/quando-l-azienda-non-paga-gli-stipendi.html
<p>Qualche anno fa <strong>l'azienda per cui lavoravo ha attraversato un periodo di crisi e lo stipendio arrivava a singhiozzo</strong> (o peggio, non arrivava proprio). Lo ricordo come uno dei periodi più brutti della mia vita.</p>
<blockquote>
<p>Per rispetto agli ex colleghi ancora coinvolti in questa vicenda, non voglio citare il nome dell'azienda che attualmente è in liquidazione; sappiate che ci sono persone che non hanno avuto la fortuna di trovare un altro lavoro, e altri che aspettano ancora i soldi dovuti.</p>
</blockquote>
<h2>I fatti</h2>
<p>Sono stato assunto da un'azienda di consulenza e, come tutti i consulenti, sono stato mandato a lavorare presso un'altra azienda "madre". <strong>Per i primi 3-4 mesi il lavoro è stato fantastico</strong>, ricordo soprattutto la spensieratezza di conoscere i nuovi colleghi e i progetti su cui dovevo lavorare.</p>
<p>Dopo i primi mesi, tuttavia, arrivarono <strong>le prime comunicazioni che lo stipendio sarebbe stato pagato in ritardo</strong> - prima 5 giorni, poi 10 giorni, poi 20 giorni... si fece dicembre e arrivò la comunicazione definitiva, <strong>"non sappiamo quando avremo modo di pagare la tredicesima e la mensilità"</strong>.</p>
<p><strong>I miei giorni lavorativi scorrevano apparentemente normali</strong>, andavo a lavoro tutti i giorni e cercavo di dare il massimo come faccio sempre; <strong>era anche difficile parlare di queste cose con i colleghi e con i capi</strong>, perché tutti gli altri erano dipendenti dell'azienda madre quindi avevo pochi colleghi diretti. L'unica persona con cui potevo sfogarmi era mia moglie (quindi immaginatevi il clima a casa).</p>
<h2>I tentativi di cambiare la mia situazione</h2>
<p><strong>La prima cosa che ho provato a fare è di trovare un altro lavoro, e in effetti l'ho anche trovato.</strong> Ho firmato una lettera di assunzione con un'altra azienda, e ho comunicato le dimissioni nei termini (45 giorni! caspita quanto ci vuole a licenziarsi quando manca l'ossigeno), <strong>ma cinque giorni prima delle dimissioni effettive mi chiama per ritrattare</strong>: <em>"Michele, aspettavamo soldi dalla regione che non sono arrivati. Se puoi, se puoi resta dove sei."</em></p>
<p>Mi è caduto il mondo addosso. <strong>Dopo aver trovato una via d'uscita, scopro che era un vicolo cieco</strong> e devo tornare indietro e rifare tutto da capo.</p>
<p><strong>Con la testa bassa vado dal responsabile del progetto</strong> a cui lavoravo <strong>e gli chiedo se è disposto a riprendermi</strong>, ovviamente valutandomi per il mio lavoro e senza alcuna presunzione di essere "perdonato". Voi cosa avreste fatto? <strong>Beh lui si è sbracciato per tenermi a lavoro</strong>, e a tre giorni dalle dimissioni ufficiali (con quintali di carte già spedite a INPS, consulenti del lavoro, etc) abbiamo ritirato tutto.</p>
<p>Contemporaneamente, la mia azienda di consulenza (quella che mi pagava lo stipendio) giurava e spergiurava di aver risolto i problemi finanziari, e quindi c'era da stare tranquilli. Ovviamente non era affatto così.</p>
<p>Dopo altri 3-4 mesi di pagamenti a singhiozzo, ritorno dal mio responsabile sul progetto - quello che mi aveva "ripreso" a seguito del casino delle dimissioni ritirate - e gli spiego per la prima volta come stanno le cose davvero. <strong>Gli dico che non ce la faccio più, che mi sembra di essere un cretino e che non so come gestire la situazione</strong>. A questo punto l'azienda "madre" mi aiuta a trovare un'altra società satellite dando di nuovo le dimissioni a 45 giorni con la determinazione che stavolta, anche a costo di rimanere senza lavoro, non avrei voluto avere più niente a che fare con un'azienda che non paga.</p>
<p>Me ne sono andato con un credito di 4500 €, e ad oggi restano solo 450€ ancora da avere (che forse non rivedrò mai più). Altri miei ex colleghi aspettano cifre più alte, dell'ordine di 10.000€ (TFR e arretrati).</p>
<h2>Come ci si sente</h2>
<p><strong>La sensazione che ho avuto nei periodi più bui era che la colpa fosse mia</strong>. Ricordo in particolare il periodo di Natale, quando non potevo comprare nemmeno un regalino a mia moglie perché avevo 4,5€ sul conto! L'inconscio mi faceva apparire che non era l'azienda ad essere nel torto, <strong>ero io a non aver fatto la cosa giusta (quale, poi?!) al momento giusto.</strong></p>
<p><strong>E' stata dura prendere qualsiasi decisione perché avevo sempre la sensazione di perderci qualcosa, ma non saprei dirvi cosa.</strong> E non vi parlo nemmeno della sensazione di irriconoscenza che ho avuto verso l'azienda "madre" del progetto, che mi hanno aiutato quando hanno potuto, e che ho provato a mollare un paio di volte. Insomma, una situazione psicologica debole.</p>
<h2>Cosa si può fare per evitare tutto questo</h2>
<p>A mente fredda è facile parlare, ma ci sono alcuni suggerimenti che posso darvi.</p>
<ol>
<li>Al primo accenno di stipendio pagato in ritardo o non pagato affatto, <strong>cercate un'alternativa</strong>. L'ideale è rassegnare le dimissioni "normali" così non perdete mensilità, TFR, e quanto altro. Non credete a chi vi dice che tutto si aggiusterà, a meno che non abbiate accesso a conti e contratti e possiate dire con tranquillità che le cose stiano davvero a posto.</li>
<li>Se avete un buon gruzzolo messo da parte e potete permettervi uno-due mesi senza stipendio (che, pur restando in azienda, accadrà), e se siete a almeno 3 mensilità arretrate, <strong>vi conviene licenziarvi IMMEDIATAMENTE per giusta causa e fare domanda di disoccupazione all'INPS.</strong> In rete si trovano <a href="http://www.guidafisco.it/mancato-pagamento-stipendio-dimissioni-870">molti articoli a riguardo</a>. E' importante sapere che se vi licenziate "normalmente" non avete diritto a niente, ma il mancato stipendio è considerabile come <em>giusta causa</em>, purché dopo iniziate una causa civile contro il datore di lavoro per riavere i soldi indietro (altra condizione affinché l'INPS eroghi la disoccupazione).</li>
<li><strong>Restare in azienda e sperare di essere licenziati è una pessima idea.</strong> Psicologicamente subirete tutto voi ed è estenuante. Anzi, non è nemmeno una tattica: è come stare su una barca in pieno oceano col mare in tempesta - o con la calma piatta: state bene finché avete viveri.</li>
</ol>
<p>Io spero che non vi accada mai nulla di tutto ciò, ma se vi è accaduto e volete provare a spiegare come vi siete sentiti e cosa avete fatto per uscirne, questo blog è a vostra disposizione. <strong>Non fate nomi di aziende perché io modero tutto e i commenti offensivi o diffamatori saranno immediatamente cancellati.</strong> Grazie per la comprensione.</p>
JS Promises: description, pros, cons of this ES6 construct
2017-03-12T13:06:29Z
https://michelenasti.com/2017/03/12/js-promises-description-pros-cons-of-this-es6-construct.html
<p>I hope that you have done some kind of exploration with Javascript, NodeJS and asynchronous constructs right now.</p>
<p>To recap: when you wait for an async operation to handle the result, for example in NodeJS when we read a file, we do this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />fs<span class="token punctuation">.</span><span class="token function">readFile</span><span class="token punctuation">(</span><span class="token string">'filename.txt'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">throw</span> err<span class="token punctuation">;</span> <br /> <span class="token comment">//now you can use the data object </span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This is the standard, classic way of handling asynchronicity in NodeJS and Javascript.</p>
<p>In the last few years a new approach has come to rise, first from outside libraries, then as part of ES6 language: <strong>Promises</strong>.</p>
<p><img src="https://michelenasti.com/images/promise.jpg" alt="" />{:style="float: none;"}</p>
<p>_Not me in the photo. This is the Italian way to promise. I love his way of looking guilty. _</p>
<h2>I promise that I will give you the result</h2>
<p>I don't want to talk about the history of promises, because like everything in JS Promises have a complex story coming from many libraries doing the same thing, with different syntaxes and patterns.</p>
<p>However ES6 has standardized this, and now we have a fantastic <code>Promise</code> object. Let's see how to use it.</p>
<p>The following snippet will create a Promise that waits 2,5 seconds and then sends a message <code>Hey. it worked</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> somePromise <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">//1</span><br /> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// only one can be called and only once </span><br /> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">'Hey. it worked'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//2</span><br /> <span class="token comment">//reject('Unable to fulfill promise'); //3</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2500</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br />somePromise<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span> <span class="token comment">//4</span><br /> <span class="token punctuation">(</span><span class="token parameter">message</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Success: '</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <br /> <span class="token punctuation">(</span><span class="token parameter">errorMessage</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Error: '</span><span class="token punctuation">,</span> errorMessage<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>My definition of promises: <strong>a <code>Promise</code> is a wrapper object that you use to encapsulate a function that might return a value in the future</strong>.</p>
<p>We declare a promise as showed in (1): <code>new Promise()</code> with a function inside.</p>
<p>This function takes two arguments that are two other functions, usually called <code>resolve</code> and <code>reject</code>. So the full specification is <code>let somePromise = new Promise((resolve, reject) => {...});</code>.</p>
<p>How do you use these two parameters? <code>resolve</code> (2) is a function used to return a value, when the function ends normally (e.g. reading a file, you get the content of the file and return to the caller); <code>reject</code> (3) is used when you encounter a problem (e.g. you couldn't read the file).</p>
<p>There are some bonds on using Promises:</p>
<ul>
<li>you cannot call both <code>resolve</code> or <code>reject</code>in your code. As soon as one of the two functions gets called, the promise stops.</li>
<li>if you don't call any of the two functions, the promise will hang.</li>
<li>you can only pass <strong>one</strong> parameter to <code>resolve</code> or <code>reject</code>. If you have more stuff to pass, wrap everything in an object.</li>
</ul>
<p>Ok, now we have defined a promise. How do we use it? An example is shown in (4): we just call the promise and then use the <code>then</code> method to use the response.</p>
<p><code>then</code> can take two functions as arguments:</p>
<ul>
<li>the first callback is called when the promise ends correctly, with the value contained in <code>resolve</code></li>
<li>the second callback is the <em>error handler</em>, so you might want to use it when you want to get data from the <code>reject</code> function.</li>
</ul>
<h2>Chaining promises</h2>
<p>This approach is nice, but the true power comes when you can <code>chain</code> many promises one after another.</p>
<p>The following code shows a function <code>asyncAdd</code> that adds two numbers after 1,5 seconds. I bet your calculator is faster. It does however some checks on the type of <code>a</code> and <code>b</code>, to be sure they are numbers.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> <span class="token function-variable function">asyncAdd</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> a <span class="token operator">===</span> <span class="token string">'number'</span> <span class="token operator">&&</span> <span class="token keyword">typeof</span> b <span class="token operator">===</span> <span class="token string">'number'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">resolve</span><span class="token punctuation">(</span>a <span class="token operator">+</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token function">reject</span><span class="token punctuation">(</span><span class="token string">'Arguments must be numbers'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1500</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token function">asyncAdd</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">)</span> <span class="token comment">// (1)</span><br /> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">result</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// (2)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token function">asyncAdd</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> <span class="token string">'33'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// (3)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// (4)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Should be 45:'</span><span class="token punctuation">,</span> res<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">errorMessage</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// (5)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>errorMessage<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p><strong>Quiz</strong>: what does this code print? (Answer below)</p>
<p>In (1) we are evaluating the first sum. <strong>Then</strong> (2) (<em>Look how it is semantically fantastic to express this concept in plain English!!!</em>) we write the result to console, and return another operation (3): we add the result to the number '33' (<strong>note! it is a string!</strong>).</p>
<p>Since we are returning a function that returns a promise, we can chain the two promises with another <code>then()</code>.</p>
<p>We didn't write the second <code>then</code> callback, because we have used the <code>.catch()</code> method. We will explain this in a second.</p>
<p>In the second <code>then()</code> (4) we would print the result of the total sum.</p>
<p>Lastly, in (5) we have the <code>catch</code> block. In the previous paragraph we were saying that you can handle errors inside every <code>then</code>, but if you chain many <code>then</code> together and the first one fails, the others are still executed. Usually you don't want this, because the next <code>then</code> depends on the result of the previous.</p>
<p>So, if you just want to stop an operation at the first error encountered, you can use a single <code>catch()</code> block at the end: whatever promise will fail, be it the first or the last, the <code>catch</code> block will be called to handle the failure. So easy to reason about.</p>
<p>(Sorry, no answer for the quiz... run it and see!)</p>
<h2>Conclusions</h2>
<p>A promise is a new construct that, after some practice, becomes a powerful tool for your async constructs.</p>
<p>The advantages are that you don't have to write anymore nested callbacks with a lot of indented code, that is often complex to reason about, and to handle errors.</p>
<p>The disadvantage is that there is much more than what I wrote in this article, about promises, that covers every possible edge case that naturally occours when you work with async code. However, what you read here should cover 80% of your needs.</p>
<p>You can transform every callback in a promise; many NodeJS core developers don't like promises at all, and use only callbacks. Popular libraries in NPM use one way or the other to deliver results, so you might be forced to use one or the other approach based on this.</p>
Two easy ways perform GET requests in NodeJS
2017-03-13T00:00:00Z
https://michelenasti.com/2017/03/13/two-easy-ways-perform-get-requests-in-nodejs.html
<p>Today I want to talk about something that we all need during our programming life: requesting data from a remote server. We will do this using callbacks and promises, to show both the approaches.</p>
<p>We will see this specifically for NodeJS because it's so simple that I couldn't even imagine.</p>
<p>In this article I'm going to talk about two super-popular libraries that do the same thing, but expose their API to the developer differently. Let's see what they do and how to use them.</p>
<p><img src="https://michelenasti.com/images/callbacks.png" alt="" /></p>
<h2>a simple use case: Google maps API</h2>
<p>Just as an example, we will write a simple program that takes a textual address (for example<code>Piazza della Concordia, Salerno</code>) and return latitude and longitude.</p>
<p>The URL is this: [https://maps.googleapis.com/maps/api/geocode/json?address=piazza della concordia, salerno](https://maps.googleapis.com/maps/api/geocode/json?address=piazza della concordia, salerno). Clicking on this link you can see the response in json format.</p>
<p>Our mission is to retrive latitude and longitude, so</p>
<pre><code>results[0].geometry.location.lat
results[0].geometry.location.lng
</code></pre>
<h2>Request: the name says it all</h2>
<p>The first library I'm going to talk is simply called <a href="https://www.npmjs.com/package/request"><code>request</code></a>. Install as always:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> <span class="token function">install</span> request <span class="token parameter variable">--save</span></code></pre>
<p>From the NPM description:</p>
<blockquote>
<p>Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.</p>
</blockquote>
<p>Here is a NodeJS function that uses <code>request</code> to perform the call</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> encodedAddress <span class="token operator">=</span> <span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://maps.googleapis.com/maps/api/geocode/json?address=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>encodedAddress<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span><br /> <span class="token literal-property property">json</span><span class="token operator">:</span> <span class="token boolean">true</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> response<span class="token punctuation">,</span> body</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /><br /> <span class="token comment">//server unreachable</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> <br /> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token string">'Unable to connect to Google Servers'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// no results </span><br /> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>body<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token string">'ZERO_RESULTS'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token string">'Unable to find that address'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>body<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token string">'OK'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">address</span><span class="token operator">:</span> body<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>formatted_address<span class="token punctuation">,</span><br /> <span class="token literal-property property">latitude</span><span class="token operator">:</span> body<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>geometry<span class="token punctuation">.</span>location<span class="token punctuation">.</span>lat<span class="token punctuation">,</span><br /> <span class="token literal-property property">longitude</span><span class="token operator">:</span> body<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>geometry<span class="token punctuation">.</span>location<span class="token punctuation">.</span>lng<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p><code>encodeURIComponent()</code> is a function that converts strings with special characters (like spaces, or quotes..) to a string that can be passed over a URI.</p>
<p><code>request</code> is just a function that takes an URI, some options, and will give the result in a <strong>callback</strong>. Since we are following Node standard approach to callbacks, where the first argument is the error object, the return callback contains a <code>undefined</code> as first argument.</p>
<h2>Axios: a Promise based equivalent</h2>
<p><a href="https://michelenasti.com/2017/03/12/js-promises-description-pros-cons-of-this-es6-construct.html">In my last article we talked about promises</a>, so we would like to see these in action, right? We have two options, the first is to wrap <code>request</code> in a promise (easy, but more code); the second is to <strong>use a library already built with promises in mind</strong>.</p>
<p>The one I use, and that is super popular, is called <strong><a href="https://www.npmjs.com/package/axios"><code>Axios</code></a></strong>: Here's the description</p>
<blockquote>
<p>Promise based HTTP client for the browser and node.js</p>
</blockquote>
<p>To install:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> <span class="token function">install</span> axios</code></pre>
<p>Here is the snippet for the axios request:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> encodedAddress <span class="token operator">=</span> <span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> geocodeUrl <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://maps.googleapis.com/maps/api/geocode/json?address=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>encodedAddress<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br /><br />axios<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>geocodeUrl<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// throw error on no results</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>status <span class="token operator">===</span> <span class="token string">'ZERO_RESULTS'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Unable to find that address'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token comment">// return new promise with data (for chaining)</span><br /> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">latitude</span><span class="token operator">:</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>geometry<span class="token punctuation">.</span>location<span class="token punctuation">.</span>lat<span class="token punctuation">,</span><br /> <span class="token literal-property property">longitude</span><span class="token operator">:</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>geometry<span class="token punctuation">.</span>location<span class="token punctuation">.</span>lng<span class="token punctuation">,</span><br /> <span class="token literal-property property">formatted_address</span><span class="token operator">:</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>results<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>formatted_address<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// show data</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">latitude: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>response<span class="token punctuation">.</span>formatted_address<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">latitude: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>response<span class="token punctuation">.</span>latitude<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">latitude: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>response<span class="token punctuation">.</span>longitude<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">//error handling </span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre>
<p>The approach here is different. <code>axios.get()</code> returns a Promise, that we chain with a call to the <code>then()</code> method.</p>
<p>Once we have the data, in the form of <code>response.data</code>, we can extrapolate the fields we need.</p>
<p>I could just print the data in the first <code>then()</code> block, but to be more coherent with my design guidelines (<em>every function does just one thing</em>) I wanted to separate the two operations so the first block will just retrieve the data, then returns another Promise (<code>return Promise.resolve({..})</code>).</p>
<p>The next <code>then()</code> block will only display the data retrieved.</p>
<h2>Which approach do you prefer?</h2>
<p>This depends on you! <strong>I prefer promises</strong>, because I find them more natural, but being a Node coder means you can choose and decide what's best for you. Both are well tested libraries with well known patterns, so you only have to choose the one you love.</p>
<p>Cheers from <code>40.7802306</code>, <code>14.7010686</code> !</p>
Deploy a NodeJS app on Heroku in less than 1 minute
2017-03-18T13:18:53Z
https://michelenasti.com/2017/03/18/deploy-a-nodejs-app-on-heroku-in-less-than-1-minute.html
<p>Actually, the time you need to deploy an app depends on how fast you are to copy & paste.</p>
<h2>Prerequisites</h2>
<p>Your code must be hosted in a git repository.</p>
<h2>Steps to set up the environment the first time</h2>
<ol>
<li>Sign up (or log in) to
<a href="http://heroku.com">heroku.com</a></li>
<li>download
<a href="https://devcenter.heroku.com/articles/heroku-cli">heroku's command for the console</a> (select the right version for your operating system).</li>
<li>be sure the command is installed: <code>heroku -v</code> (you should see some output)</li>
<li>launch <code>heroku login</code>and log in with your email and password.</li>
<li>Upload ssh keys to heroku: <code>heroku keys:add</code></li>
<li><code>ssh -v git@heroku.com</code>: even if the command seems to fail, check that the long output contains the row</li>
</ol>
<pre><code>debug1: Authentication succeeded (publickey).
</code></pre>
<p>Generally it is one of the last 5-6 rows. If this log is present, everything is set up correctly.</p>
<h2>Steps to set up the environment with the NodeJs app</h2>
<ul>
<li>Set the host port of your application as an environment variable:</li>
</ul>
<pre><code>const port = process.env.PORT || 3000 ;
...
app.listen(port);
</code></pre>
<ul>
<li>set the <code>npm start</code> script, in <code>package.json</code>:</li>
</ul>
<pre><code>"scripts": {
"start": "node server.js",
...
}
</code></pre>
<ul>
<li>Check that the app runs correctly: <code>npm start</code></li>
<li>launch <code>heroku create</code>: will create a new remote for your repository named <code>heroku</code></li>
<li><code>git push heroku</code> : will push your code to heroku</li>
<li><code>heroku open</code>: will open your webapp in the browser</li>
</ul>
<p>For every modification you do to your code, just commit and <code>git push heroku</code>!</p>
L'Inglese tra italiani, a lavoro, è una scelta sbagliata
2017-03-20T09:56:01Z
https://michelenasti.com/2017/03/20/l-inglese-tra-italiani-a-lavoro-e-una-scelta-sbagliata.html
<p>C'è un trend che ogni tanto ritorna, ossia quello di usare l'inglese come lingua ufficiale durante l'orario di lavoro. Con colleghi italiani. In un ufficio italiano.</p>
<p>La prima volta che ho sentito di questa pratica era durante la mia prima esperienza lavorativa; realizzando un software per un'azienda inglese, una grande parte delle specifiche arrivavano in quella lingua (giustamente!) e i nostri Business Analyst dovevano interfacciarsi continuamente con loro per documentare il software. Da qui, la scelta del team di analisti di parlare solo in inglese, tra di loro, per un giorno a settimana.</p>
<p>Ora che lavoro da remoto, e interazione "sincrona" con i colleghi è molto scarsa, per uno specifico progetto è stato concordato di comunicare (in chat!) solo ed esclusivamente in inglese.</p>
<p><img src="https://michelenasti.com/images/pexels-photo-119689%20(1).jpeg" alt="" /></p>
<p><em>it's time for a cup of tea!</em></p>
<p>Chi vi scrive ha un inglese decente, complice il papà professore di Inglese che si è sbattuto per insegnarglielo fin da piccolo. Ovvio che non sono madrelingua ma non faccio pena, anzi mi sento tranquillamente a mio agio a parlare il mio fantastico <em>English</em> con accento napoletano.</p>
<p>Ebbene, <strong>trovo questa idea di sforzarsi a parlare inglese, tra italiani, una cagata pazzesca.</strong></p>
<h2>I tipi di comunicazione</h2>
<p>Faccio solo un rapido e semplice excursus sul tipo di comunicazioni possibili in un ambiente di lavoro:</p>
<ul>
<li><strong>Comunicazione Sincrona</strong> - è quel tipo di comunicazione che prevede due o più persone impegnate nello stesso momento. Esempi: la classica chiacchierata face-2-face, la telefonata, una videocall (skype...) etc. Qui è importante l'immediatezza e la chiarezza dei concetti espressi.</li>
<li><strong>Comunicazione Asincrona</strong> - Le comunicazioni che non prevedono una risposta immediata tra gli interlocutori. Tra questi tipi di comunicazione ci sono chat, mail, blog post, commenti a un wiki, documentazione, documenti word... Qui invece è desiderabile ottenere una comunicazione non ambigua e chiara anche a distanza di mesi.</li>
</ul>
<p>Aspettarsi una risposta istantanea a una chat è da stupidi, così come vi sembrerebbe piuttosto strano se un interlocutore fisico (con cui state conversando al distributore) smetta di rispondervi all'improvviso.</p>
<p>Bene, avete appena completato il primo corso base di comunicazione aziendale :)</p>
<h2>Perché parlare in inglese tra italiani è una cagata pazzesca</h2>
<p>Ho sentito molti motivi per cui le aziende vogliono introdurre l'inglese aggressivo tra i propri dipendenti. Smontiamoli uno ad uno.</p>
<ul>
<li><strong>"Così possiamo attrarre talenti stranieri"</strong>. Cazzata. I talenti stranieri non vengono perché in Italia si parla inglese, anzi si aspettano che con loro parliate in inglese (che li avete chiamati a fare?). Per attrarre talenti stranieri servono <strong>idee sfidanti</strong>, <strong>stipendi alti</strong>, e un <strong>ecosistema dinamico.</strong></li>
<li><strong>"Così i dipendenti migliorano l'inglese".</strong> Mi dispiace davvero che qualcuno pensi che, se due persone che non conoscono l'inglese iniziano improvvisamente a parlarlo (sotto tortura), poi lo imparano. Anche questa è una <strong>cazzata</strong>. Se volete imparare l'inglese esiste una sola strada: un bel corso di lingue in cui si parla molto (e in cui la grammatica non è trascurata).</li>
<li><strong>"Così ci sentiamo più internazionali"</strong>. Equivale a misurarsi il pene col righello senza mai confrontare le misure con gli altri. L'unica metrica che dovrebbe interessarvi è il FATTURATO.</li>
</ul>
<p>Aggiungo, per completezza, che sul luogo di lavoro è importante che la comunicazione sia <strong>efficace, semplice e chiara</strong>. <strong>Ogni sovrastruttura, impedimento, legaccio, costrizione finirà col logorare il vostro lavoro.</strong> Io ad esempio non sopporto quando devo parlare con qualcuno e mi si risponde "mandami una mail che sono SEMPRE impegnato" (non riesci neanche a trovare 10 minuti per me?). Anche questo è un esempio di costrizione che non migliora il problem solving.</p>
<h2>L'estremo opposto: only Italian, please</h2>
<p>Siccome sono un programmatore (e se siete su questa pagina dovreste averlo intuito) posso già dirvi che il 99% dei libri su cui ho studiato, gli articoli on line che ho letto (e scritto) , le guide tecniche e i siti delle aziende più importanti sono in inglese.</p>
<p>Questo vuol dire sostanzialmente due cose:</p>
<ul>
<li><strong>La documentazione dei vostri software e dei vostri processi di business non può essere in italiano</strong>. Anche se oggi siete tutti italiani sul progetto, un bel giorno potreste trovarvi ad assumere degli stranieri, o delocalizzare lo sviluppo in qualche paese asiatico, e l'unico modo che avete per comunicare con loro sarà l'inglese. Siate future-proof e usate l'inglese come fa il resto del mondo.</li>
<li><strong>Il vostro codice sorgente deve essere in inglese.</strong> Il motivo è lo stesso del punto precedente, con l'aggravante che <code>checkIfCodiceFiscaleIsValid()</code> non si può sentire.</li>
</ul>
<p>E' importante dunque che chi voglia lavorare in questo mondo debba per lo meno saper leggere e scrivere un Inglese decente.</p>
<h2>Quando è giusto parlare inglese (o qualsiasi altra lingua straniera)</h2>
<p>Quando nella vostra conversazione sincrona uno dei partecipanti non capisce l'italiano. A quel punto si sceglie una lingua compresa da tutti e la si usa.</p>
<h2>Implicazioni filosofiche di questa scelta</h2>
<p>Siamo costretti a restare confinati nella nostra italianità? Non avremo mai accesso ai "talenti stranieri"?</p>
<p>Se la tua azienda è una s.r.l., non puoi certo definirla L.T.D. :)</p>
<p>Un altra conseguenza è che non è la lingua che si parla a rendere <em>grande</em> un workplace.</p>
<p>Infine: meglio un ottimo performer con un inglese zoppicante, o un inglese madrelingua che lavora così così?</p>
Easy testing of NodeJS applications with Mocha
2017-03-23T18:13:00Z
https://michelenasti.com/2017/03/23/node-js-testing-easy-with-mocha.html
<p>Testing in Javascript has become an immensely popular argument, but still there's a lot of people that has not clear how to do it effectively. <a href="https://michelenasti.com/2016/08/my-very-personal-javascript-fatigue/">I was one of these</a>. There are many troubles that a young developer has to overcome in order to master Javascript testing. In this article, I'm going to explain Javascript testing with Mocha, a powerful library.</p>
<h2>Why testing in Javascript</h2>
<p>An easy example. You write a function that takes only one argument, and use it extensively. After some days, you modify this function to take two arguments.</p>
<p>Obviously you try to find every place where this function is called and add the missing parameter. Since the scripting nature of the language, how can you tell that this modification did not break anything? How to discover rapidly what to fix? Are users our only testers?</p>
<h2>Setup the example</h2>
<p>Let's write a simple Node test file. Let's call it <code>utils.js</code>, a file where we put all the functions that do not fit in a specific module.</p>
<pre><code>module.exports.add = (a,b) =&gt; a + b;
</code></pre>
<p>That's it! Let's test this function. Is our sum function really doing its job?</p>
<h2>Setup the test</h2>
<p>First, let's install
<a href="https://mochajs.org"><strong>Mocha</strong></a>. This is easy as launching</p>
<pre><code>$ npm install --save-dev mocha
</code></pre>
<p>Let's modify our <code>package.json</code> (you did run <code>npm init</code>, didn't you?) to add a new task:</p>
<pre><code>"scripts": {
...
"test": "mocha **/*.test.js"
},
</code></pre>
<p>Mocha will be installed as a <em>development dependency</em>, this means that it is not necessary to run our code. Some deployment systems (like Heroku) will not download these dev dependencies.</p>
<p>As you can see, the <code>test</code> command will execute all files in every directory ending with <code>.test.js</code>. This is incredibly fast.</p>
<p>Let's create in the very same directory where we put <code>utils.js</code>, another file called <code>utils.test.js</code>.</p>
<p>Here is the test code:</p>
<pre><code>const utils = require('./utils'); //(1)
it('should add two numbers', () =&gt; { //(2)
let res = utils.add(33,11);
if (res !== 44) {
throw new Error(`Expected: 44, but got ${res}`); //(3)
}
});
</code></pre>
<p>And now you can simply run <code>npm test</code>. you should see an out like this:</p>
<pre><code>$ npm test
&gt; node-tests@1.0.0 test /Users/michelenasti/Doc
uments/node-tests
&gt; mocha **/*.test.js
✓ should add two numbers
1 passing (13ms)
</code></pre>
<h2>What's going on</h2>
<p>Our tests are run directly by Mocha, that's why we don't import the library in this function.</p>
<p>Mocha provides us a function called <code>it()</code>. You might notice this function at (1). It takes two parameters: a description of the test, and a function with the code to execute.</p>
<p>In the JS community tests are usually written in a <em>Behaviour Driven Development</em> paradygm, this is a complex notion, for now let's just say that test description starts with "<em>should ...</em>". I'll write more in the future :)</p>
<p>The second argument passed to <code>it()</code> is a function with the actual test code. Here we call the function under test, take the result and check the correctness.</p>
<p>How Mocha decides if a test has passed or not? If no errors are thrown in our test code, then the test is marked as passed. On the other side, if an <code>Error()</code> object is thrown, the test will stop and the message of the <code>Error</code> is printed to the terminal.</p>
<p><em>Exercise for you: modify <code>utils.js</code> to let the test fail :)</em></p>
<h2>Do JS developers throw errors to test their functions?</h2>
<p>No, in reality JS developers NEVER throw Errors. Usually in JS we use some other libraries to <em>assert</em> some properties on the result, using functions that are more expressive. We will talk about this aspect in another post. However, I think that seeing how tests work without assertion libraries is <em>fundamental</em> to use such libraries.</p>
<h2>Last but not least</h2>
<p>Do you want your test to run automatically everytime you change some file? Of course you can, mixing some code coming from a previous post (<a href="https://michelenasti.com/2017/01/31/develop-faster-in-nodejs-with-nodemon.html">nodemon</a>).</p>
<p>In <code>package.json</code> add the script <code>test-watch</code>...</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token string-property property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"test"</span><span class="token operator">:</span> <span class="token string">"mocha **/*.test.js"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"test-watch"</span><span class="token operator">:</span> <span class="token string">"nodemon --exec \"npm test\""</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<p>Happy testing!</p>
Become a test expert in NodeJS with these tricks
2017-03-27T10:55:00Z
https://michelenasti.com/2017/03/27/become-a-test-expert-in-nodejs-with-these-tricks.html
<p>After reading my first guide to
<a href="https://michelenasti.com/2017/03/23/node-js-testing-easy-with-mocha.html">testing NodeJS with Mocha</a> you might have grasped the fundamental concepts of NodeJS testing. However, <strong>real word code is usually tested with some other expedients</strong> that you might know to be a better tester (and coder).</p>
<h2>Using an assertion library</h2>
<p>In my previous article I wrote how to check the test result: to set a test as failing, you throw a <code>new Error()</code> object with the message to show.</p>
<p>This is the naive approach; the default is to use an assertion library. These libraries will expose an API that is clearer and simpler to manage, and will let you test more conditions with less code. Under the hood, they will launch the <code>new Error()</code> if the conditions are not respected.</p>
<p>Let's see one of these libraries. One of the most famous libraries is called
<a href="https://github.com/mjackson/expect"><code>expect</code></a>:
<blockquote>
<p>When you use expect, you write assertions similarly to how you would say them, e.g. "I expect this value to be equal to 3" or "I expect this array to contain 3". When you write assertions in this way, you don't need to remember the order of actual and expected arguments to functions like assert.equal, which helps you write better tests.</p>
</blockquote></p>
<p>Let's see an example. Here is the test for a <code>add</code> function, and the test is contained in file <code>utils.test.js</code>:</p>
<pre><code>//utils.test.js
const expect = require('expect');
const utils = require('./utils');
it('should add two numbers', () => {
let res = utils.add(33, 11);
expect(res).toBe(44).toBeA('number');
});
</code></pre>
<p>Easier to write, and to reason about. with <code>expect</code> you can also check if objects have properties, etc. Have a look on their website to see all aviable methods.</p>
<h2>Testing async code</h2>
<p>If you're using NodeJS, or Javascript, you're also probably using async functions. No matter if it's in the form of promises or callbacks, stuff in JS happens async, and we must deal with it.</p>
<p>Let's prepare an example async function:</p>
<pre><code>//utils.js
module.exports.asyncAdd = (a,b, callback) => {
setTimeout(() => {
callback(a+b);
}, 1000);
}
</code></pre>
<p>How do we test it? The first approach we might think would be to write the test like before:</p>
<pre><code>//utils.test.js
it('should add two numbers', () => {
let res = utils.asyncAdd(33, 11, (res) => {
// will this work?
expect(res).toBe(44).toBeA('number');
});
});
</code></pre>
<p>If you launch this example, <em>the test will pass, but for the <strong>wrong reason</strong></em>. Infact, Mocha will not wait the result callback an will end the test instantly. Try to break the test or the function... Mocha will say that everything is ok. That's not good.</p>
<p>How can we fix this? Mocha has a super-simple solution, just add a <code>done</code> argument to the test callback. When <code>done</code> is present, Mocha will not end the test before you call the <code>done()</code> function. Let's try:</p>
<pre><code>//utils.test.js
it('should add two numbers', (done) => {
let res = utils.asyncAdd(33, 11, (res) => {
expect(res).toBe(44).toBeA('number');
done();
});
});
</code></pre>
<p>Now, if you try to break the test or the function, you'll see that Mocha will fail. Exactely what we want.</p>
<h2>Testing an Express application</h2>
<p>It's very difficult for the random developer to write a NodeJS app without using <a href="https://expressjs.com/it/">Express</a>, a framework for web applicatons that allows you to write REST endpoints easily.</p>
<p>How do I test an express application?</p>
<p>the creators of Express have come in help by creating another library called <a href="https://github.com/visionmedia/supertest">supertest</a>:</p>
<blockquote>
<p>HTTP assertions made easy via superagent.</p>
</blockquote>
<p>Let's write a simple http application with Express:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//file server.js</span><br /><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">404</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">error</span><span class="token operator">:</span> <span class="token string">'Page not found'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'hello baby'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />module<span class="token punctuation">.</span>exports<span class="token punctuation">.</span>app <span class="token operator">=</span> app<span class="token punctuation">;</span> <span class="token comment">// (1)</span></code></pre>
<p>This simple express app will return a <code>404</code> error with a json payload, everytime you navigate to <code>http://localhost:3000/</code>. Why? Because there's nothing to see, obviously! :p</p>
<p>The only point worth of noting is that we simply export the app, as every other node module. Adding this line at (1) does not break anything, and makes testing possible.</p>
<p>To test this app, let's write the test using <em>supertest</em>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//server.test.js</span><br /><br /><span class="token keyword">const</span> request <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'supertest'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> expect <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'expect'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./server'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should return hello world response'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">done</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">request</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span> <span class="token comment">// (1)</span><br /> <span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span> <span class="token comment">// (2)</span><br /> <span class="token comment">//supertest expect!!</span><br /> <span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token number">404</span><span class="token punctuation">)</span> <span class="token comment">// (3)</span><br /> <span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// (4)</span><br /> <span class="token comment">//expect library! </span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>res<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toInclude</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">error</span><span class="token operator">:</span> <span class="token string">'Page not found'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span>done<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// (5)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>That's interesting. At (1) we are importing the express app and using <code>supertest</code> to wrap it. Then we perform a <code>get</code> request over <code>/</code> (2) and start expecting things about the result. Unfortunately, supertest has another <code>expect</code> method that is not related to the one in the <code>expect</code> library.</p>
<p>By the way, with supertest we can make assumptions about the status code (3), and if we want to assert something about the body of the request, we can do like in (4): when using a callback we can do everything over the <code>res</code> variable, and infact we are using the <code>expect</code> library to see if the body includes the <code>error</code> property.</p>
<p>Since this Mocha test is async, we need a way to tell mocha that the test has ended. In (5) we see that supertest is already aware of mocha and will stop the test by passing the <code>done</code> function to <code>end()</code>.</p>
<h2>Conclusions</h2>
<p>For a novice, the problem of testing Node apps is that there are many libraries the same thing. For an expert, this becomes an advantage: you can choose the best for your purposes (but you must know them in advance).</p>
<p>By the way, testing is important. Test everything is testable. Otherwise, maintaining javascript code can only be a mess.</p>
Fantastic Unit Tests in Javascript with Mocks
2017-04-07T00:00:00Z
https://michelenasti.com/2017/04/07/fantastic-unit-tests-in-javascript-with-mocks.html
<p>Let's start with the example. We have two files, one that is the main one called <code>app.js</code> that exports just one method, called <code>handleSignup()</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// file app.js</span><br /><span class="token keyword">const</span> db <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./db.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />module<span class="token punctuation">.</span>exports<span class="token punctuation">.</span><span class="token function-variable function">handleSignup</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">email<span class="token punctuation">,</span> password</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token comment">// save the user to the database</span><br /> db<span class="token punctuation">.</span><span class="token function">saveUser</span><span class="token punctuation">(</span><span class="token punctuation">{</span> email<span class="token punctuation">,</span> password <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token comment">// ...</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This <code>app.js</code>, at some point, calls the <code>db.saveUser()</code> function. Let's see the <code>db</code> module:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// file db.js</span><br />module<span class="token punctuation">.</span>exports<span class="token punctuation">.</span><span class="token function-variable function">saveUser</span> <span class="token operator">=</span> <span class="token parameter">user</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Saving the user...'</span><span class="token punctuation">,</span> user<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Nothing very special, it's just a demo. Let's go with the important question.</p>
<h2>How do we unit-test app.js?</h2>
<p>For those not used to <strong>unit testing</strong>, it <strong>is a way of testing classes atomically</strong>. This means that app.js may depend on other classes, but we will not test these.</p>
<p>Let me be more specific. the <code>db</code> module could be really calling a database, this means that if the database connection is not working, the test would fail. OR, if the test is <em>really</em> saving a user, next time we launch the test it could be failing because the user is already in the db.</p>
<p>To handle all these scenarios, we will <strong>mock</strong> the <code>db</code> module. We will provide a fake implementation of <code>db</code> that will reply as we will, every time. This way, <em>we will be testing only app.js code</em>.</p>
<h2>But... what if db module has a bug?</h2>
<p>You <strong>MUST</strong> test the <code>db</code> module, too! However, it's not a good practice to test the <code>db</code> module by testing the <code>app</code> module.</p>
<p>If you don't test the <code>db</code> module, and all of a sudden your test has a bug, how can you say where the bug is? Imagine this in a layer of 10-15 modules... you get the complexity.</p>
<p>However, let me point out that <strong>unit testing alone is not the solution to all of your problems</strong>. Unit testing can test that a single class will perform as specified, but another kind of testing (I call it "integration testing") should be responsible to check that everything is wired correctly.</p>
<p>Here is a famous gif about this concept:<img src="https://michelenasti.com/images/unit-testing-1.gif" alt="" /></p>
<h2>1. Before showing the code, let me introduce to you: rewire()</h2>
<p>To test <code>app.js</code> we need <a href="https://www.npmjs.com/package/rewire"><code>rewire</code></a>, a nodejs module.</p>
<p>To install:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> i --save-dev rewire </code></pre>
<p><code>rewire()</code> works like the <code>require()</code> function, this mean that you <em>could</em> use rewire for your applications, even if it does not make any sense. It is indeed very useful in tests.</p>
<p>When you <code>rewire</code> a module, the module is imported <em>but</em> some new methods are added: these are <code>__get__()</code> and <code>__set__()</code>. Whith these two methods you can retrive any local variable instantiated locally.</p>
<p>Example:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> rewire <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'rewire'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><br /><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">rewire</span><span class="token punctuation">(</span><span class="token string">'app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//like require but...</span><br /><br /><span class="token keyword">const</span> db <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">__get__</span><span class="token punctuation">(</span><span class="token string">'db'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// will return the db object! </span></code></pre>
<h3>Rewire has a problem with const</h3>
<p>You might be tempted to do this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">...</span><br /><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">rewire</span><span class="token punctuation">(</span><span class="token string">'app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> db <span class="token operator">=</span> <span class="token punctuation">{</span> <br /> <span class="token function-variable function">saveUser</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br />app<span class="token punctuation">.</span><span class="token function">__set__</span><span class="token punctuation">(</span><span class="token string">'db'</span><span class="token punctuation">,</span> db<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//this will not work in this case!</span></code></pre>
<p>Since <code>db</code> is declared as a <code>const</code> in <code>app</code>, <code>rewire</code> cannot do the magic: <code>const</code> variables in javascript are not reassignable.</p>
<p>However, even if the <code>db</code> object is not reassignable, we can still modify its internals, as long they are declared as <code>let</code> and <code>var</code>. That's <strong>why we will mock single functions and not the whole object</strong>.</p>
<h2>2. Before showing the code, let me introduce to you: expect.createSpy()</h2>
<p>I have already talked about the <a href="https://michelenasti.com/2017/03/27/become-a-test-expert-in-nodejs-with-these-tricks.html"><code>expect</code></a> assertion library.</p>
<p>The good thing is, <strong><code>expect</code> has also a way to create mocks - that they call spies</strong>.</p>
<p>What is a <em>spy</em>? it's a function that can substitute the real implementation. It can be "trained" to return the value you want, to simulate errors, throw exceptions, etc.</p>
<p>With spies, you can also check that the function has been called, with the right parameters.</p>
<h2>TL;DR - Here is the CODE</h2>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//file app.test.js </span><br /><span class="token keyword">const</span> expect <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'expect'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> rewire <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'rewire'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> app <span class="token operator">=</span> <span class="token function">rewire</span><span class="token punctuation">(</span><span class="token string">'./app'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// (1) </span><br /><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'App'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">//mock</span><br /> <span class="token keyword">let</span> db <span class="token operator">=</span> app<span class="token punctuation">.</span><span class="token function">__get__</span><span class="token punctuation">(</span><span class="token string">'db'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// (2) </span><br /> db<span class="token punctuation">.</span>saveUser <span class="token operator">=</span> expect<span class="token punctuation">.</span><span class="token function">createSpy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// (3) </span><br /> <br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should call saveUser with user object'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> email <span class="token operator">=</span> <span class="token string">'michele@example.com'</span><span class="token punctuation">;</span><br /> <span class="token keyword">let</span> password <span class="token operator">=</span> <span class="token string">'123abc'</span><span class="token punctuation">;</span><br /><br /> app<span class="token punctuation">.</span><span class="token function">handleSignup</span><span class="token punctuation">(</span>email<span class="token punctuation">,</span> password<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// (4) </span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>db<span class="token punctuation">.</span>saveUser<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHaveBeenCalledWith</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <br /> email<span class="token punctuation">,</span><br /> password<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /></code></pre>
<p>You should find familiar what happens at (1): we are importing the app module with <code>rewire</code>.</p>
<p>then, we are (2) using the <code>__get__()</code> function to retrieve the <code>db</code> object from the module. At (3) we are replacing the function <code>saveUser()</code> with a spy, created by us.</p>
<p>The test is created as always; we prepare some input parameters and pass them to <code>app.handleSignup()</code>.</p>
<p>At (4) we can check that the spy has been called for real with the function <code>toHaveBeenCalledWith</code> and we can check also that the parameters are the same!</p>
<h2>There's more...</h2>
<p>But that's enough for now! Testing is something I really like, because it can tell you instantly if you're doing something wrong. The overall quality of a project is greatly improved when you test and know how to test.</p>
All my articles about Testing in Javascript in one post
2017-04-08T13:23:03Z
https://michelenasti.com/2017/04/08/all-my-articles-about-testing-in-javascript-in-one-post.html
<p>Hey there,</p>
<p>in the last month I have done a great work to understand how testing in javascript could be done in a effective, elegant way.</p>
<p>Here are all my articles about it:</p>
<ul>
<li><a href="https://michelenasti.com/2017/03/23/node-js-testing-easy-with-mocha.html">Easy testing of NodeJS applications with Mocha</a></li>
<li><a href="https://michelenasti.com/2017/03/27/become-a-test-expert-in-nodejs-with-these-tricks.html">Become a test expert in NodeJS with these tricks</a></li>
<li><a href="https://michelenasti.com/2017/04/07/fantastic-unit-tests-in-javascript-with-mocks.html">Fantastic Unit Tests in Javascript with Mocks</a></li>
</ul>
<p>Enjoy!</p>
Diventare Senior™ con Javascript: le funzioni filter, map, reduce
2017-04-14T10:09:00Z
https://michelenasti.com/2017/04/14/javascript-filter-map-reduce.html
<p>Si può lavorare per anni con javascript senza mai arrivare a conoscere tre funzioni fantastiche (e standard) che permettono di scrivere codice veloce e testabile.</p>
<p>E' vero che si diventa senior principalmente con l'esperienza, ma se dite di aver lavorato per 3+ anni con JS dovete anche mostrare che avete un'<em>esperienza di un certo livello</em>.</p>
<p><strong>Aprite la console del browser e provate gli esempi!</strong></p>
<h3>A partire da un array, come filtriamo solo gli elementi pari senza usare il ciclo for?</h3>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> filtered <span class="token operator">=</span> arr<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">elem</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> elem <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">===</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>filtered<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p><code>filter()</code> prende in input una funzione di <em>test</em> che viene eseguita su ogni elemento dell'input: se la funzione restituisce <code>true</code>, <code>elem</code> viene aggiunto all'array in output.</p>
<h3>Come raddoppiamo tutti gli element di un array senza usare il ciclo for?</h3>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> mapped <span class="token operator">=</span> arr<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">elem</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> elem <span class="token operator">*</span> <span class="token number">2</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>mapped<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Anche qui la funzione <code>map()</code> prende in input una funzione che viene eseguita su ogni elemento dell'input. Per ogni elemento, questo viene <em>trasformato</em> in un altro valore in base al risultato della funzione.</p>
<h3>Come sommiamo tutti gli elementi di un array senza usare il ciclo for?</h3>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> reduced <span class="token operator">=</span> arr<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">prev<span class="token punctuation">,</span> elem</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> prev <span class="token operator">+</span> elem <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>reduced<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p><code>reduce()</code> è più complessa da spiegare, ma poi sarà facilissima da usare: permette di applicare una funzione <em>N-a-1</em> dove da un array tiriamo fuori un unico risultato. Nel nostro esempio abbiamo applicato la somma (voi come esercizio potreste provare a tirare fuori la media!).</p>
<p><code>reduce()</code> prende in input due elementi: una funzione e un elemento di partenza (Sarebbe lo <code>0</code> specificato in fondo). Per capire cosa accade, facciamo un esempio sull'array <code>[8, 7]</code>.</p>
<p>Alla prima iterazione viene chiamata la funzione con <code>prev = 0</code> e <code>elem = 8</code>: siccome stiamo iterando sul primo elemento e non c'è nessun elemento precedente, viene usato il secondo argomento di <code>reduce</code> come valore di inizio (ricordate quello <code>0</code> lì in fondo?). A questo punto viene eseguita la somma, che è <code>8</code>.</p>
<p>Alla seconda iterazione, la funzione viene chiamata con <code>prev = 8</code> (risultato dell'iterazione precedente!) e <code>elem = 7</code>: Anche in questo caso verrà eseguita la somma che è <code>15</code>, che sarà anche l'output di <code>reduce</code>.</p>
<h3>(Domanda implicita del lettore: Perchè tutto st'accanimento contro il ciclo for?)</h3>
<p>Il ciclo for è un ottimo costrutto per iterare gli array, tuttavia non fa capire a chi legge cosa sta accadendo, oltre al fatto di non essere manutenibile. e componibile.</p>
<p>Supponiamo di voler filtrare tutti i numeri pari da un array; successivamente raddoppiarli e poi sommarli (ossia, la composizione delle tre domande precedenti).</p>
<p>Con le funzioni presentate prima, ecco la soluzione:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> result <span class="token operator">=</span> arr<br /> <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">elem</span><span class="token punctuation">)</span> <span class="token operator">=></span> elem <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">elem</span><span class="token punctuation">)</span> <span class="token operator">=></span> elem <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">prev<span class="token punctuation">,</span> elem</span><span class="token punctuation">)</span> <span class="token operator">=></span> prev <span class="token operator">+</span> elem<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>E' ovvio che si può scrivere anche con un bel (paio di) for. Ma quante righe ci vogliono? e rileggendolo dopo sei mesi, si capisce?</p>
<p>Per concludere, con la svolta funzionale di altri linguaggi (chi ha detto Java?!) questi concetti sono stati applicati anche altrove, proprio per la loro semplicità e componibilità. Magari hanno altri nomi, ma il funzionamento è lo stesso. Cercateli e ...usateli!</p>
"Vorrei, ma ho paura che vada male" : come assumersi dei "rischi intelligenti"
2017-04-21T00:00:00Z
https://michelenasti.com/2017/04/21/vorrei-ma-ho-paura-che-vada-male-come-assumersi-dei-rischi-intelligenti.html
<p>Oggi vorrei parlarvi di <strong>come assumersi dei rischi intelligenti</strong>. Intelligenti in che senso?!</p>
<p>Questo post ce l'ho in testa da molto tempo, almeno da quando ho letto il libro <a href="http://amzn.to/2oaDAB6">Startup Of You</a> di Reid Hoffman (fondatore di LinkedIn) e Ben Casnocha. Per i duri di comprendorio inglese qui c'è la <a href="http://amzn.to/2oQsY92">versione in Italiano</a>.</p>
<p>Nel libro gli autori spiegano cosa si può fare per <strong>conferire alla propria vita e alla propria carriera un atteggiamento "da startup"</strong>: ci sono una lunga serie di consigli, discussioni, esempi, e insomma è uno dei libri che ho letto con più piacere.<img src="https://michelenasti.com/images/rischio-investimenti-definizione-749x400.jpg" alt="" /></p>
<h2>I rischi intelligenti</h2>
<p><em>Siamo abituati a pensare ai rischi come qualcosa di negativo</em>: perdite di denaro sui mercati azionari, o guida di una moto senza casco. Ma il rischio non è un nemico; è qualcosa che fa intrinsecamente parte della nostra vita.</p>
<p>Le situazioni rischiose, comunque, sono quelle in cui la possibilità che le cose vadano male è decisamente più alto del vantaggio che si ottiene se le cose vanno bene.</p>
<p>Ad esempio, <strong>volare in aereo non è rischioso</strong>, perché anche se lo scenario peggiore è *molto *<em>doloroso</em> la possibilità che questo accada è bassissima. Invece, il tempo di viaggio risparmiato è un dato di fatto.</p>
<p>Alcuni imprenditori sono dei cowboy del rischio; tuttavia, <strong>ciò che distingue un imprenditore giudizioso da un semplice spavaldo non è l'alta tolleranza al rischio, ma l'abilità di saperlo valutare e gestire correttamente</strong>.</p>
<p>Il rischio, in un contesto come quello del lavoro, è correlato alle scelte che facciamo, e alla possibilità che qualcosa vada storto. Ad esempio, quando cambiamo lavoro, ci assumiamo il rischio che il nuovo possa essere anche peggiore del vecchio, e invochiamo tutte le divinità affinché non sia così!</p>
<p>Ma non solo tutto ciò che facciamo comporta rischi; <strong>anche</strong> <strong>ciò che NON facciamo è altamente rischioso</strong>. Questo vale specialmente quando il mondo intorno a noi sta cambiando e richiede un nostro adattamento.</p>
<h2>Valutare i rischi</h2>
<p><strong>Valutare i rischi è difficile, ma non impossibile. Gli imprenditori lo fanno tutti i giorni.</strong> Tuttavia, essi non usano analisi matematiche o economiche (degne di Wall Street!) per prendere decisioni; sinceramente, non dovremmo farlo neanche noi.</p>
<p>Ecco alcuni princìpi guida:</p>
<h3>1. stiamo esagerando nella valutazione?</h3>
<p>Il primo passo nella valutazione di un rischio è ricordarsi che <strong>il caso peggiore probabilmente non è così brutto, o così probabile, come sembra</strong>.</p>
<h3>2. Il caso peggiore è tollerabile?</h3>
<p>Se accade il caso peggiore cosa succede alla nostra vita? La reputazione scenderà a livelli sotto zero, perderemo tutti i soldi, la carriera sarà definitivamente finita?</p>
<p>Se nel caso peggiore accade che perdiamo <em>un po'</em> di soldi, o veniamo licenziati (posto che riusciamo a trovare un altro lavoro subito), o rischi semplicemente di sentirti inadeguato, <strong>dovresti comunque provarci</strong>. Almeno finché hai un <a href="https://michelenasti.com/2015/11/serve-un-piano/">solido Piano Z</a>.</p>
<h3>3. Puoi cambiare la decisione a metà dell'opera? Puoi passare al piano B?</h3>
<p>Quando vediamo che le cose si stanno mettendo male, possiamo fare delle scelte che ti permettono di tornare indietro e ricominciare?</p>
<h3>4. Non confondiamo l'incertezza con il rischio</h3>
<p>Nessuno può sapere come andrà prima del tempo. Ma questo non significa che l'incertezza sia un rischio! <strong>Le opportunità migliori sono quelle con i più grandi punti interrogativi</strong>.</p>
<h3>5. Considera la tua età e a che fase della tua vita ti trovi. Quali saranno i rischi tra un paio d'anni?</h3>
<p>Fallire da ragazzi è considerato formativo; è per questo che sono i ventenni a fondare le aziende più strampalate.</p>
<p>Man mano che invecchi, la tua percezione del rischio cambia. Arrivano i bambini, la casa, l'auto, etc.</p>
<p><strong>Meglio fare qualcosa di rischioso prima che sia troppo tardi</strong> ;)</p>
<h2>Facciamo un rapido test...</h2>
<p>Prendetevi 30 secondi per ogni punto sottostante. Provate a immaginare i pro e i contro delle seguenti situazioni. Considerati i cinque punti espressi precedentemente, come cambierebbe la tua vita se:</p>
<ul>
<li>trovi un lavoro fantastico e strapagato ma è negli USA</li>
<li>la tua azienda smette di pagare gli stipendi</li>
<li>inizi a dirigere un team</li>
</ul>
<p>Questo semplice esercizio mentale magari vi farà riflettere su come percepite il rischio; nel mio caso mi ha aiutato a fare scelte più consapevoli nel mondo del lavoro.</p>
<p>In bocca al rischio ;)</p>
Agile? No grazie, noi siamo Lean
2017-05-04T00:00:00Z
https://michelenasti.com/2017/05/04/agile-no-grazie-preferisco-lean.html
<p>Agile, agile, agile... ormai tutti fanno Agile, implementando un framework tipo Scrum (che è uno dei possibili modi di essere Agile).</p>
<p>Per dare una definizione: siete _Agile_® se la vostra metodologia di sviluppo segue il <a href="http://agilemanifesto.org/iso/it/manifesto.html"><em>Manifesto Agile</em></a>.</p>
<p>Tuttavia, è facile prendere un framework (ripeto: Scrum) e sputtanarlo per tirare fuori qualcosa che non va. Spesso ciò accade quando il management continua a ragionare a delivery fisse, chi-fa-cosa, butta-a-mare-la-qualità-dobbiamo-consegnà, etc.</p>
<p><img src="https://michelenasti.com/images/geografia_metodologie.png" alt="Quest'immagine prova a spiegare com'è fatto il mondo" /></p>
<h2>Ma in questo post dovevi parlare di LEAN</h2>
<p>Avete ragione.</p>
<p>Ritorniamo sulle definizioni. <em>Lean</em> (in italiano: magro, scarno) è un termine preso in prestito dal settore manifatturiero, e consiste in una serie di principi da seguire per ottenere <em>qualità, velocità & soddisfazione del cliente</em>. (Stesse finalità di Agile!)</p>
<p>Il punto è che i principi base del <em>Lean thinking</em> non sono filosofici, ma pratici. Vediamoli:</p>
<ul>
<li><strong>Eliminate Waste</strong>: fate riunioni che non servono? Non fatele. Scrivete documenti che nessuno leggerà? Non scriveteli. Più chiaro di così!</li>
<li><strong>Build Quality In</strong>: Mettere in pratica tutte quelle pratiche che portano a evitare i bug, o a correggerli subito. Correggerli dopo è sempre peggio.</li>
<li><strong>Create Knowledge</strong>: All'inizio di un progetto difficilmente avrete idea di cosa vuole il cliente. Inoltre, ciò che sapete voi è molto diverso da ciò che conosce il vostro collega. Creare conoscenza significa soprattutto <em>condividerla</em>.</li>
<li><strong>Defer Commitment</strong>: Prendere le decisioni all'ultmo momento utile (ossia quando non sono più rinviabili) permette di avere la maggior conoscenza possibile, e quindi di prendere decisioni migliori.</li>
<li><strong>Deliver Fast</strong>: Prima fate vedere il prodotto al cliente, prima saprete se è quello che vuole.</li>
<li><strong>Respect People</strong>: non significa solo evitare di mandare a cagare il tuo collega; significa anche rispettare le scelte del gruppo, riconoscerne gli sforzi e aiutarli quando gli sforzi non producono frutti.</li>
<li><strong>Optimize the Whole</strong>. Ottimizzare il processo, nella sua interezza, significa applicare i punti precedenti. Si deve ottimizzare continuamente, a ogni livello.</li>
</ul>
<p>Fin qui era filosofia. Ma praticamente che dobbiamo fare?</p>
<h2>Lean in pratica: gli step zero</h2>
<p>Per essere Lean nel mondo del software ci sono due <strong>step zero</strong>:</p>
<ul>
<li><strong>Source code management</strong> - perchè il codice non si condivide più via email o via floppy da un bel pò. Tools come SVN (antichi!) o GIT dovreste conoscerli tutti ormai.</li>
<li><strong>Scripted builds</strong> - il vostro prodotto deve essere generabile a partire da <strong>un solo comando</strong>. Ancora meglio: ogni volta che committate, un ambiente di <em>continuous integration</em> dovrebbe far partire una build automatica e verificare che sia tutto ok. Non dimenticate di mandare una mail a chi ha rotto la build ;)</li>
</ul>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><strong>create knowledge</strong> - il codice è in un solo posto</li>
<li><strong>eliminate waste</strong> - nessun lavoro manuale per compilare e deployare il progetto!</li>
<li><strong>build quality in</strong> - l'automazione elimina possibili cause d'errore.</li>
</ul>
<p>Vediamo ora altre pratiche che possono aiutarvi a diventare <em>Lean</em>.</p>
<h3>Il meeting giornaliero</h3>
<p>Se dovessi scegliere un unico punto da implementare, tra tutti i punti di questo articolo, il meeting giornaliero è sicuramente quello che sceglierei io. <em>La comunicazione tra membri del team è dannatamente importante.</em></p>
<p>Per fare un daily meeting perfetto bisogna:</p>
<ul>
<li>farlo tutti i giorni, alla stessa ora</li>
<li>non deve superare 15 minuti</li>
<li>partecipano e parlano tutti</li>
<li>quando parla qualcuno non si fanno domande e non si interrompe</li>
<li>si risponde a tre domande: <em>cosa ho fatto ieri?</em>, <em>cosa farò oggi?</em>, <em>che problemi ho avuto?</em></li>
<li><em>facoltativo</em>: fare il meeting in piedi (così durerà anche meno di 15 minuti)</li>
</ul>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><em>respect people</em> - stiamo mettendo il team al centro dello sviluppo. Tutti si sentiranno di far parte di una squadra.</li>
<li><em>create knowledge</em> - Parlare di ciò che si sta facendo è il modo migliore di creare una conoscenza condivisa.</li>
</ul>
<h3>Testing automatico</h3>
<p>Qui parliamo dell'<strong>esecuzione automatica dei test a partire da un singolo comando</strong>, con tanto di report a fine esecuzione. In questo modo avremo test che sono sempre eseguiti nello stesso modo e che non sono soggetti a errori umani.</p>
<p>I test dovrebbero essere lanciati sia dai singoli sviluppatori mentre realizzano le feature, che dall'ambiene di continuous integration quando committiamo.</p>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><em>Build Quality In</em> - test automatici eseguiti regolarmente aiutano a prevenire i difetti.</li>
<li><em>Eliminate Waste</em> - i bug vengono trovati prima, e quindi corretti prima.</li>
<li><em>Create Knowledge</em> - i test sono un modo per documentare come funziona il codice.</li>
</ul>
<h3>Continuous Integration</h3>
<p>La <strong>continuous integration</strong> è una pratica che consiste nell'integrare i piccoli cambiamenti al codice in maniera frequente. Ciò cerca di ridurre, o addirittura eliminare, la fase di integrazione post-sviluppo.</p>
<p>Per farlo:</p>
<ul>
<li>Usate una macchina (vera o virtuale) per il sistema di CI.</li>
<li>a ogni commit, la CI deve far partire una build e i test.</li>
<li>I developer devono aggiornare il codice una volta al giorno; una volta all'ora è meglio.</li>
<li>Le build fallite devono essere subito sistemate.</li>
</ul>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><em>Build Quality In</em> - in Continuous Integration, siamo sicuri che il codice sia sempre funzionante.</li>
<li><em>Eliminate Waste</em> - E' più efficiente integrare continuamente piccole modifiche che una singola grande fase di integrazione finale.</li>
</ul>
<h3>Less Code</h3>
<p>"Less Code" significa <strong>scrivere solo il codice necessario per implementare la funzionalità richiesta</strong>. Qui stiamo ribadendo il principio della semplicità: se una cosa è piccola è anche facile capirla; un codice sorgente vasto diventa difficile non solo da capire, ma anche testare e debuggare. Scrivere (<em>poco</em>) codice in modo semplice è un'arte. Per poter scrivere meno codice bisogna:</p>
<ul>
<li><strong>eliminare codice inutile</strong>. Facile a dirsi 😊 In pratica bisogna trovare il codice, o le <em>forze</em> che creano il codice non necessario, ed eliminarlo. Ogni codice aggiunto al repository deve essere <em>giustificabile</em>. Sviluppa solo per l'iterazione o il task corrente, non per quelle del futuro che verrà. (Verrà?)</li>
<li><strong>Migliorare l'efficienza del codice</strong>. Ciò si fa applicando degli standard o comunque le best practices.</li>
</ul>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><em>Build Quality In</em> - Codice che fa quello che deve fare. Cosa vuoi di più?</li>
<li><em>Eliminate Waste</em> - Codice che non serve, o mega-design-patterns che non si capiscono (o che si capiscono solo nella mente dell'autore), sono scoraggiati.</li>
</ul>
<h3>Short Iterations</h3>
<p>Prima si sviluppavano applicazioni con un ciclo di rilascio da 6 mesi a 1 anno; adesso la moda è 15/30 giorni. Questo tipo di rilasci permette ai clienti di capire se stiamo realizzando davvero il software di cui hanno bisogno, e permette di capirlo <em>prima che sia troppo tardi</em>.</p>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><em>Eliminate Waste</em> - rilasci frequenti permettono di concentrarsi solo su ciò che serve davvero.</li>
<li><em>Deliver Fast</em> - Il nuovo codice viene rilasciato al cliente ad intervalli regolari.</li>
</ul>
<h3>Customer Partecipation</h3>
<p>In passato il cliente veniva coinvolto solo per le specifiche, a inizio progetto, e poi ci si vedeva alla fine per mostrare il risultato. E lì sentivi frasi "ma non era quello che volevo" 😫</p>
<p>Usando un approccio <em>Lean</em> invece il cliente fa parte del team. E' il cliente a fornire i requisiti, a prioritizzarli, e ad accettarli a fine iterazione.</p>
<p><strong>Principi Lean rispettati</strong>:</p>
<ul>
<li><em>Create Knowledge</em> - Attraverso la collaborazione col cliente, i requisiti vengono determinati prima e meglio.</li>
<li><em>Defer Commitment</em> - Siccome il cliente è coinvolto nel processo, è possibile prendere le decisioni architetturali solo quando servono.</li>
</ul>
<h2>Lean è uguale o diverso da Agile?</h2>
<p>Chi ha fatto un po' di Agile starà ora dicendo: "Ma tutte queste cose le facciamo anche noi!". Già! Dov'è la differenza?</p>
<p>Le differenze sono sostanzialmente due: <em>scope</em> e <em>focus</em>.</p>
<p>In <strong>Agile</strong> lo scopo è di migliorare lo sviluppo del software e si focalizza sull'adattabilità e sul rilascio veloce. Oppure, come piace dire a me, si parte dalla filosofia e si trovano i mezzi per metterla in pratica.</p>
<p>Nell'approccio <strong>Lean</strong> invece si guarda al quadro generale, e ci si focalizza più sulle pratiche (che sono il punto di partenza!) che sulla filosofia. Infatti <strong>non è obbligatorio mettere in pratica contemporaneamente tutte le pratiche</strong>, si può tranquillamente iniziare a integrarle una alla volta; e soprattutto, si può seguire qualsiasi tipo di metodologia ma usare le pratiche <em>Lean</em>.</p>
<p>Per questo, <strong>negli ultimi team con cui ho collaborato</strong>, piuttosto che mettere insieme tutte le cerimonie di Scrum <strong>ho preferito privilegiare quelle pratiche che sicuramente avrebbero portato un valore effettivo</strong>.</p>
<p>Non avrei conosciuto le pratiche Lean senza Agile. <strong>Non potrei fare Agile senza mettere in pratica i principi Lean. Al contrario, si può essere Lean senza essere Agile</strong>. Questi due mondi si sovrappongono per molte cose e sono, alla fine della fiera, <strong>complementari tra loro</strong>.</p>
I colloqui di lavoro nelle Big
2017-05-22T15:08:19Z
https://michelenasti.com/2017/05/22/i-colloqui-di-lavoro-nelle-big.html
<p>Recentemente sono stato speaker all'evento <a href="https://www.meetup.com/it-IT/GDGCampania/events/239723850/">pre-Google I/O</a> organizzato dal <a href="http://gdgcampania.com/">Google Developers Group Campania</a>, a Napoli.</p>
<p>Il mio talk verteva sui colloqui di lavoro delle aziende top, e qui ci sta bene un bel disclaimer: <em>non ho mai fatto colloqui per aziende top - tranne un piccolo frangente con Amazon</em>. Ma ho letto molto sull'argomento, e ho chiesto in giro ad alcuni amici che hanno fatto le selezioni per queste aziende, quindi avevo un po' di materiale che era bello condividere.</p>
<p><!-- markdownlint-disable MD033 -->
<iframe src="https://docs.google.com/presentation/d/1eQce7aEOl8qaGPbmhQVmUQb99Sf2bioBJhmuEER4_B8/embed?start=false&loop=false&delayms=60000" frameborder="0" width="480" height="389" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
<!-- markdownlint-enable MD033 --></p>
<p>Una parte del talk riguardava alcuni "rompicapi" che in passato, più che oggi, venivano sottoposti agli aspiranti candidati. Fermo restando che agli intervistatori non interessa sapere la risposta (ammesso che esista!), semmai vogliono vedere in che modo approcciate i problemi, che ragionamenti fate, insomma quanta creatività avete.</p>
<p>Torno a ribadire: queste domande sono "rare" e tutte le persone in carne ed ossa con cui ho parlato non le hanno ricevute; tuttavia, dato che esiste un libro sull'argomento (<a href="http://amzn.to/2rKTgbo">"sei abbastanza sveglio per lavorare in google?", di William Poundstone</a>), e considerato che sono un appassionato di indovinelli, non ho potuto ignorare.</p>
<p>A fine talk molti sono venuti per chiedermi le "soluzioni" (che ripeto: non sempre esistono) o gli approcci; altri sono venuti a pormi degli indovinelli, ai quali per mia fortuna sono riuscito a trovare la risposta in tempo (ed evitare la figura di merda 😅 ).</p>
<p>Eccovene alcuni!</p>
<h3>Dado senza dado</h3>
<p>Sai simulare un dado con una monetina? Puoi lanciare la moneta più volte.</p>
<h3>I Filosofi bugiardi</h3>
<p>Attorno a un tavolo sono seduti tanti filosofi. Alcuni dicono sempre la verità; altri solo bugie.</p>
<p>A turno, ogni filosofo prende la parola e dice: "Il filosofo alla mia sinistra è un bugiardo!"</p>
<p>Quando tutti i filosofi hanno espresso questo importantissimo concetto, prende la parola un altro filosofo ed esclama: "siamo 21 seduti a questo tavolo!"</p>
<p>Il suo vicino ribatte: "Non è vero! Siamo 22!"</p>
<p>Che diamine stanno dicendo?! Quanti sono? Chi ha ragione? Come sono seduti?</p>
<h2>Le risposte di mia moglie</h2>
<p><strong>Dado senza dado:</strong></p>
<blockquote>
<p>Con la moneta entro in un negozio e compro il dado.</p>
</blockquote>
<p><strong>Problema dei filosofi bugiardi:</strong></p>
<blockquote>
<p>Cambio tavolo: vado a sedermi da un'altra parte.</p>
</blockquote>
<p>Posso dirvi che in aziende come Google queste risposte non sarebbero state solo degli <em>ice-breaker</em>, ma verrebbero probabilmente considerate nel giudizio globale del candidato.</p>
<p>Tuttavia non basta per superare il colloquio 😅 Scrivi la tua risposta nei commenti!</p>
Cinque tool per migliorare la vostra developer experience
2017-05-30T12:07:50Z
https://michelenasti.com/2017/05/30/cinque-tool-per-migliorare-la-vostra-developer-experience.html
<p>Riuscireste a svolgere il vostro lavoro senza la linea di comando? Se siete come me, ossia amanti di bash, ecco alcuni tool che vi permetteranno di migliorare la vostra <em>developer experience</em>.</p>
<h3><code>mac</code> command line</h3>
<p>Si <a href="https://github.com/guarinogabriel/Mac-CLI">installa da linea di comando</a> e crea un eseguibile, <code>mac</code>, che permette di svolgere molte operazioni comuni con pochi click.</p>
<p><img src="https://michelenasti.com/images/demo-mac-cli.gif" alt="" /></p>
<p>Un esempio?</p>
<ul>
<li><code>mac ip:public</code> vi restituisce il vostro ip pubblico</li>
<li><code>mac speedtest</code> esegue uno speed test da linea di comando</li>
<li><code>mac memory</code> vi dice quale app sta consumando la vostra preziosa RAM</li>
</ul>
<p>e tanti altri. Sono circa una cinquantina!</p>
<h3><code>Oh-my-zsh</code> per un terminale più combattivo</h3>
<p><a href="https://github.com/robbyrussell/oh-my-zsh">Oh-my-zsh</a> è un tool che va a sovrascrivere un po' di cose nei vostri file di configurazione di zsh. In particolare, modifica il funzionamento del prompt di comando mostrandovi le informazioni di cui più avete bisogno:</p>
<p><img src="https://michelenasti.com/images/oh-my-zsh.jpg" alt="" /></p>
<p>Insomma, avrete sott'occhio user, cartella in cui vi trovate, repository e branch, senza impazzire. E poi migliora anche l'utilizzo dell'autocompletamento (il TAB). Ci sono anche i plugin. Oh my!</p>
<h3><code>ngrok</code>, per testare chiamate API dall'esterno</h3>
<p>Dovete testare le chiamate di un sistema esterno verso il vostro software? Ad esempio, io ho avuto questa esigenza con le API di Paypal e Gestpay: a un certo punto questi sistemi dovevano notificarmi che i pagamenti erano andati a buon fine e mi avrebbero contattato agli indirizzi configurati.</p>
<p>Come testare e come verificare il contenuto di queste chiamate? Facile, con <a href="https://ngrok.com/">ngrok</a>: un tool che permette di creare un tunnel privato da un indirizzo http esterno.</p>
<p><img src="https://michelenasti.com/images/ngrok.png" alt="" /></p>
<p>Per farla facile, voi lanciate <code>ngrok http 8000</code> e ngrok creerà un indirizzo tipo <code>http://abc33344422.ngrok.io</code>. Visitando questo indirizzo (anche dall'esterno della vostra rete privata!) ngrok redirige il traffico a<code>localhost:8000</code>. Tutto ciò che passa per ngrok viene loggato e mostrato!</p>
<h3>Git Flow, una metodologia per semplificare e standardizzare git nel team</h3>
<p>Git flow è sia <a href="https://github.com/nvie/gitflow">un tool installabile da linea di comando</a>, sia un approccio filosofico alla domanda "come lavorare in gruppo".</p>
<p>Git è un potentissimo sistema di versioning e branching; spero per voi che lo stiate già utilizzando altrimenti brrrr.</p>
<p>Con git flow organizziamo il lavoro in due <em>meta-filoni</em>:</p>
<ul>
<li>branch <code>master</code>, che corrisponde alle versioni rilasciate</li>
<li>branch <code>develop</code>, ossia dove gli sviluppatori committano le feature sviluppate, in attesa di rilascio.</li>
</ul>
<p>Oltre a questi due branch "generali", ogni azione degli sviluppatori è realizzata creando nuovi branch:</p>
<ul>
<li>Gli sviluppi delle singole feature sui branch <code>feature/*tua_feature*</code></li>
<li>I rilasci (da develop a master) su branch chiamati <code>release/*nome_release*</code></li>
<li>le hotfix (fix, correzioni a bug, di versioni già rilasciate) su branch <code>hotfix/*nome_fix*</code>.</li>
</ul>
<p>Non ci avete capito niente? <a href="https://danielkummer.github.io/git-flow-cheatsheet/">Qui un grafico che prova a spiegare comandi e funzioni</a>.</p>
<h3>Bonus: Postman per testare le interfacce Rest</h3>
<p>Ho iniziato a lavorare che c'era SOAP e si stava appena iniziando a parlare di REST. Qualche anno dopo, SOAP è diventato <em>legacy</em> e tutti si dichiarano REST, chi bene e chi male.</p>
<p>Ad ogni modo, è sorta l'esigenza di testare le interfacce REST e come testarle se non con <a href="https://www.getpostman.com/">Postman</a>?</p>
<p><img src="https://www.getpostman.com/img/v2/logo-big.svg" alt="Postman" /></p>
<p>Questa è una vera e propria applicazione <em>standalone</em> che permette di effettuare ogni tipo di chiamata desideriate, GET - PUT - POST - HEAD etc. C'è la possibilità di salvare le chiamate, passare header aggiuntivi, visualizzare i dati in molti modi (ad esempio JSON), etc etc.</p>
<p>Come dite? non è un tool da linea di comando? Avete ragione, ma questo è il mio blog e qui comando io 😂</p>
We' waglio', vuoi venire a lavorare nella mia startup?
2017-06-09T09:20:50Z
https://michelenasti.com/2017/06/09/we-waglio-vuoi-venire-a-lavorare-nella-mia-startup.html
<p>Ricevo almeno <strong>due richieste al mese</strong> per collaborare con startup.
Più o meno il copione è sempre lo stesso:</p>
<ul>
<li><em>"Ciao Michele, non ci conosciamo, ma ti ho trovato on line e ho pensato di parlarti della mia startup..."</em></li>
<li>"Dimmi!"</li>
<li><em>"Abbiamo questa idea rivoluzionaria, sorprenderemo il mondo, <strong>non abbiamo competitor</strong>, etc etc"</em></li>
<li>"Fantastico! Dove posso vedere una demo?"</li>
<li><em>"No, per ora non abbiamo ancora nulla. Anzi, stavamo proprio cercando qualcuno che voglia collaborare con noi per realizzarla... Per caso sei interessato?"</em></li>
<li>"Quindi non c'è nulla? solo un'idea nella tua testa?"</li>
<li><em>"C'è molto più dell'idea! Ho quasi fatto tutto, manca solo il prodotto!"</em></li>
<li>"..."</li>
<li><em>"Ti darò una quota importante della società, il 30%"</em></li>
<li>"..."</li>
</ul>
<h2>Michele, sei troppo spocchioso</h2>
<p>Non so se ve ne ho mai parlato ma il mio rimpianto più grande è quello di <strong>non</strong> aver lavorato in una startup, da neolaureato; ma questa è un'altra storia che approfondirò successivamente.</p>
<p>Ciò che sbagliano tutte le persone che mi contattano (e giuro che sono <em>almeno</em> due al mese) è che pretendono che io debba rimanere affascinato dal loro racconto e quasi quasi debba chiedere io di lavorare per loro. Non funziona così.</p>
<p>Innanzitutto, <strong>la mia tolleranza al rischio si è abbassata di molto</strong>. Ora che ho una figlia ci penso 10 volte prima di prendere qualsiasi scelta che possa compromettere non la mia vita, ma la sua. Per questo motivo <em>il momento migliore in cui si deve lanciare (o lavorare per) una startup è subito dopo l'università</em>, quando si è ancora ragazzini e c'è un fantastico paracadute, ossia mamma & papà.</p>
<p><strong>Lavorare per una startup implica un periodo più o meno lungo di lavoro senza un'entrata fissa</strong>, che in genere arriva quando si realizza una delle seguenti condizioni:</p>
<ul>
<li>Un investitore ci mette i soldi</li>
<li>La startup trova dei clienti paganti</li>
</ul>
<p>Trovare un investitore è difficile: in Italia i fondi di investimento disposti a finanziare qualcosa si contano sulle dita di una mano. E se ti danno il cash, te lo danno quando hai almeno un MVP o una demo da mostrare.</p>
<p>Per questo penso che in Italia convenga partire con l'idea di fatturare prima possibile, per quanto difficile questo possa sembrare.</p>
<p>Motivo per cui scarto tutte le startup che non sanno rispondere alla domanda <strong>"come intendi fare soldi?"</strong>.</p>
<h2>Tra CEO e CTO deve esserci qualcosa di più forte di un matrimonio</h2>
<p>Leggevo un interessante articolo in cui si diceva che il 65% delle startup non falliscono perchè non trovano clienti o investitori, bensì perchè <a href="http://money.cnn.com/2014/02/24/smallbusiness/startups-entrepreneur-cofounder/index.html">i fondatori litigano</a>.</p>
<p>Mettiamoci nei panni dell'aspirante startupper che ho citato prima: contatta persone a caso su facebook chiedendogli di lavorare alla sua idea.</p>
<p>Troverà mai qualcuno? Gli auguro di si, ma non penso proprio che succederà in maniera così scontata. Ma chi?</p>
<p>Dovendo lavorare con qualcuno per lanciare un'azienda, con l'ansia costante di finire i soldi, le persone che fanno nascere una startup dovrebbero essere:</p>
<ul>
<li>affiatate tra di loro</li>
<li>avere lo stesso obiettivo</li>
<li>persone che si fanno guidare dai "numeri" e non dai sentimenti.</li>
</ul>
<p>Una coppia di sposi <a href="http://fundersandfounders.com/startup-dirty-laundry-conflicts-that-kill-partnerships/">litiga in media 321 volte in un anno</a> (Statistiche USA eh, io e mia moglie litighiamo molto meno 😂) eppure nel migliore dei casi restano insieme per tutta la vita; Tra fondatori di una startup deve esserci un grado di alchimia più forte, visto che in questo caso non c'è amore ma ambizione.</p>
<p>E qui andiamo a finire su un'altra fissa dei founder: <strong>sono così convinto che debba funzionare, che funzionerà</strong>. Purtroppo non funziona così. Lo startupper eccellente fa esperimenti continui, verifica con i numeri che le sue ipotesi erano corrette, e se sono sbagliate, cambia modello di business.</p>
<h2>Perchè non riesco a trovare sviluppatori per la mia startup?</h2>
<blockquote>
<p>Tutti quelli che contatto mi dicono che sono impegnati o che non stanno valutando di cambiare lavoro. Io però offro di diventare capi in un'azienda che sarà anche loro. Perchè non accettano?</p>
</blockquote>
<p>I motivi possono essere tanti e diversi. Ad esempio:</p>
<ul>
<li><strong>Il rischio</strong>. Lo sviluppatore non vede prospettive nell'idea proposta e teme di rimanere con nulla in mano nel giro di poco.</li>
<li><strong>La sensazione di inadeguatezza.</strong> Chi sono io per mettermi a fare il "capo" in una startup?</li>
<li><strong>La mancanza di risparmi accantonati</strong>. Senza soldi, è impossibile lavorare per un lungo periodo senza entrate.</li>
<li><strong>Quando si propone uno stipendio, questo è molto basso</strong>. Un altro motivo per cui bisogna cercare tra i neolaureati.</li>
<li><strong>Quando lo stipendio è basso, si propongono le equity</strong>. Per me le equity di un progetto in cui non credo valgono zero.</li>
<li><strong>Perché sembra che il grosso del lavoro lo debba fare proprio io</strong>. Se il mio fantomatico CEO dice di aver fatto "tutto", tranne il prodotto, mentre io svilupperò lui che fa?</li>
<li><strong>Perchè io a questo CEO non lo conosco e non so se mi sta antipatico</strong>. Come fai a <em>fidanzarti</em> con uno che non conosci?</li>
</ul>
<h2>E' possibile superare questi problemi? Troverò sviluppatori per la mia idea?</h2>
<p>Esci di casa, vai ai meetup, partecipa agli eventi, stringi mani, prenditi una birra con queste persone. Non sarà veloce, nè facile, ma sicuramente funziona meglio della random chat con sconociuti.</p>
Quanto è complicato essere Elon Musk
2017-06-29T00:00:00Z
https://michelenasti.com/2017/06/29/caspiterina-quanto-e-complicato-essere-elon-musk.html
<p>Stamattina ho "audioascoltato" <a href="http://amzn.to/2ullR8A">un libro su Elon Musk</a>, che è davvero un personaggio fuori dalle righe. Mi hanno colpito un paio di passaggi.</p>
<p><img src="https://michelenasti.com/images/FabMelodia-MarteELONmusk.jpg" alt="" /></p>
<ul>
<li>Da adolescente studiava solo le materie che riteneva davvero necessarie, come matematica e fisica; in tutte le altre si accontentava della sufficienza.</li>
<li>Passava i weekend a leggere, dai fumetti alla fantascienza. Anche 2 libri al giorno!</li>
<li>Veniva bullizzato perchè aveva quel famoso atteggiamento "so tutto io"</li>
<li>E nella sua prima azienda, da lui fondata, era a capo dello sviluppo tecnico. Si confrontava tutti i giorni con persone che non avevano le sue spiccate doti e si incazzava quando gli sviluppatori non lo seguivano, non lo capivano, o lo contraddicevano. Addirittura gli sviluppatori, quando tornavano al lavoro al mattino, trovavano il loro lavoro cambiato o completato perchè Elon ci aveva messo mano!</li>
</ul>
<p>Insomma, un personaggio davvero fuori dalle righe.I progetti più rivoluzionari vengono dalla testa di quest'uomo: l'<strong>auto elettrica</strong>, il <strong>razzo spaziale riutilizzabile</strong>, la <strong>capsula-treno superveloce</strong>...</p>
<p>Qual è la differenza tra un genio come lui, e un genio qualsiasi? Elon (ormai siamo amici) ha uno spiccato senso degli affari. La sua intelligenza l'ha sempre utilizzata per ricavarci qualcosa, dal punto di vista economico. Ecco alcuni esempi:</p>
<ul>
<li>A 12 anni ha venduto il suo primo videogioco piuttosto rudimentale, per 550$.</li>
<li>Al college, oltre a riparare pc e a scrivere piccoli software per gli amici, aveva trasformato il dormitorio in un night club in cui si pagava 5€ l'ingresso. Il sabato registravano anche 500 ingressi!</li>
<li>Da adolescente stava per aprire una sala giochi col fratello, progetto arenatosi quando nessun adulto ha voluto mettere la firma per gli adempimenti normativi.</li>
<li>Aveva meno di 30 anni ed era già multimilionario.</li>
</ul>
<p>Musk ha davvero stravolto le idee che avevamo sulla genialità: in futuro lo paragoneremo a un Leonardo Da Vinci, o a un Einstein? Chissà.</p>
Recensione del libro: Codice Montemagno
2017-07-17T00:00:00Z
https://michelenasti.com/2017/07/17/recensione-del-libro-codice-montemagno.html
<p>Il libro di cui vi parlo oggi è <a href="http://amzn.to/2uqzV4j">Codice Montemagno</a>.</p>
<p><strong>Marco <em>Monty</em> Montemagno</strong> è un esperto di social media e di comunicazione italiano. Da circa 15-20 anni si occupa di tutto ciò che è tecnologia & comunicazione. Ha fondato la piattaforma Blogo nel 2005, che poi ha venduto, e ora diciamo che lavora <em>in proprio</em>.</p>
<p>Un altro elemento per comprendere l'autore è la sua <strong>presenza on line</strong>. Da circa 2 anni Marco pubblica un breve video quotidiano, in cui parla di business, comunicazione, digital, social, strategie, etc. Da quando ha iniziato quest'opera di divulgazione è passato da 7000 a 200.000 fan su facebook. A ogni video che posta fa centinaia di commenti e like. Perché?</p>
<p>Oltre che puntare ai contenuti, che sono di indubbia qualità, Marco ha ridefinito lo standard della comunicazione digitale in Italia. Ha creato un nuovo linguaggio visivo che, dobbiamo ammetterlo, è stato copiato in lungo e in largo dagli altri.</p>
<h2>Ma parliamo di Codice Montemagno</h2>
<p>Se quindi conoscete Montemagno per i suoi trascorsi on line, non vi dovrebbe stupire il fatto che nel momento in cui ha "annunciato" il libro, quindi senza aver spedito neanche una copia, già era in cima alla classifica dei libri più venduti su Amazon.</p>
<p>Il libro è una versione riveduta e corretta, anzi meglio organizzata, di quanto ha già proposto online. Nel libro ci sono tantissime citazioni di altri libri (quanto leggi, monty?!) da cui partono una serie di riflessioni e come possiamo applicare questo genere di info al nostro business, che sia tradizionale o innovativo.</p>
<p>Mi hanno in particolare stupito un paio di paragrafi. Uno che analizzava il fenomeno Trump dal punto di vista mediatico e come fosse possibile prevedere, un anno prima delle elezioni, che le avrebbe vinte lui nonostante gli scandali. Questo perchè Trump è un personaggio mediatico straordinario (ricordate Berlusconi?) e quindi è un maestro nell'antica arte di "spararla grossa".</p>
<p>Un altro spunto interessante riguarda il ruolo che i video avranno nel futuro dei social. Il testo ormai sta scomparando, complice anche una generazione di analfabeti funzionali che ci circonda (questa opinione è mia, non del Monty). I video invece funzionano perchè sono diretti, perchè Facebook li predilige rispetto al testo, e soprattutto perchè la gente li guarda. Ho suggerito a mia moglie di applicare questo consiglio dei video alla sua attività e lei ha potuto davvero confermare che il tasso di interazione ai suoi post è aumentato.</p>
<h2>Un giudizio più ragionato</h2>
<p>Se non avete mai visto nulla di Montemagno, questo libro sarà per voi una sorpresa, specialmente se avete a che fare col digitale.</p>
<p>Se come me, avete visto una decina di suoi video (considerate che ne ha fatti più di 400!), il libro vi ripresenterà le idee in forma più chiara, discorsiva, organica.</p>
<p>I contenuti hanno secondo me senso a prescindere dal personaggio che li propone, anche se detti da lui che li ha vissuti in prima persona assumono un ulteriore senso di realtà.</p>
<p>Se invece pensi che i video di Montemagno siano banali, o li hai già visti tutti almeno due volte, puoi anche risparmiarti di comprare il libro: ti sembrerà di rileggere ciò che hai già sentito.</p>
E' giusto fidarsi dei remote workers?
2017-07-19T00:00:00Z
https://michelenasti.com/2017/07/19/e-giusto-fidarsi-dei-remote-workers.html
<p>Cari amici, ho scritto <a href="https://medium.com/bemind-me/e-giusto-fidarsi-dei-remote-workers-987ecc2d65be">un articolo per il blog di Bemind</a> sul tema del lavoro remoto e la fiducia tra capo e dipendente, ossia: possiamo fidarci dei lavoratori remoti? Cliccate sul link per leggerlo.</p>
L'innovazione non copia
2017-07-27T09:39:00Z
https://michelenasti.com/2017/07/27/il-libro-che-tutti-i-tecnologhi-dovrebbero-leggere-from-zero-to-one.html
<p>Negli ultimi (in realtà anche <em>primi</em>) 5 anni di carriera lavorativa, quante volte avrò detto "<strong>dai ragazzi, ci basta pensare a una bella idea e faremo soldi a palate</strong>"? Ecco, se siamo ancora tutti <em>povery</em> ci sarà un motivo. E il libro <a href="https://www.amazon.it/segreti-startup-ovvero-costruisce-futuro/dp/8817080462/ref=as_li_ss_tl?ie=UTF8&qid=1501144633&sr=8-1&keywords=da+zero+a+uno&linkCode=ll1&tag=ilblodimicnas-21&linkId=7e1eada6bd03b1ae3cd088013adf3372">From Zero to One</a> (da me linkato nella versione italiana, <em>Da Zero a Uno</em>) prova a spiegarci qual è.</p>
<h2>Chi è Peter Thiel</h2>
<p>Peter Thiel, autore del libro, è uno dei <strong>fondatori di Paypal</strong>. Quella cricca di personaggi che all'epoca l'ha venduto a Ebay è oggi una delle <em>gang</em> di miliardari più potenti del mondo. E il bello è che sono tutti <em>imprenditori seriali</em>, quindi tutti si sono lanciati in altre aziende, chi come CEO chi come investitore.</p>
<p><img src="https://michelenasti.com/images/Peter_Thiel_flag.jpeg.jpeg" alt="Peter Thiel" /></p>
<p>La fortuna di noi informatici e non solo è che possiamo apprendere tanto da loro, visto che hanno scritto libri interessantissimi e di forte impatto: sul mio blog ho già parlato dei <a href="https://michelenasti.com/2015/11/i-tre-pilastri-per-migliorare-la-propria-carriera/">libri di Reid Hoffman</a> e della <a href="https://michelenasti.com/2017/06/29/caspiterina-quanto-e-complicato-essere-elon-musk.html">vita di Elon Musk</a>, altri membri della gang.</p>
<p>Dopo aver venduto Paypal, Thiel ha lanciato un suo fondo di investimento e tra le aziende che ha direttamente finanziato ce ne sono due che fruttano svariati milardi: una di queste è una piccola startup californiana di nome <strong>Facebook</strong>, e un'altra è meno nota (opera nel settore dei big data) chiamata <strong>Palantir</strong>.</p>
<p>Insomma, Thiel è uno che di business se ne intende.</p>
<h2>Cosa mi ha colpito del libro</h2>
<p>Ci sono tantissimi spunti interessanti. Non voglio ripeterveli capitolo per capitolo, ma alcune cose sono da insegnare nelle facoltà di economia & informatica di tutto il mondo:</p>
<ul>
<li>
<p>Una startup che inventa qualcosa di nuovo, che rivoluziona un mercato, va <strong>da zero a uno</strong>: crea un nuovo business dove prima non c'era nulla. Chi invece migliora un prodotto già esistente va <strong>da uno a n</strong>, e nell'opinione di Thiel così non farai mai il botto.</p>
</li>
<li>
<p>Thiel ci tiene a precisare che andando da zero a uno creerai un monopolio, e nella sua visione i monopoli non sono un male: anzi, sono l'unico modo per fare soldi veri. Teniamo presente che comunque i migliori monopoli non dureranno all'infinito (10 anni?).</p>
</li>
<li>
<p>Uno dei criteri che usa Thiel nello scegliere le startup da finanziare è: <strong>il prodotto che mi si sta proponendo migliora di dieci volte altre soluzioni sul mercato?</strong> Infatti secondo Thiel se non stiamo migliorando di almeno <strong>10x</strong>, è inutile perderci tempo (e cita il caso delle startup che trattavano pannelli solari, che miglioravano le soluzioni esistenti di 1.5-2x. ).</p>
</li>
<li>
<p><strong>E' una soluzione a un problema reale?</strong> Nessuno vuole pagare per un prodotto che non serve, o in cui non vede alcun valore.</p>
</li>
<li>
<p><strong>Come fare molti utenti in fretta?</strong> Adesso risponderemmo in coro "Growth Hacking!" (recensione in arrivo!), ma questo è un problema da risolvere subito, addirittura quando la startup viene fondata. Thiel suggerisce di <strong>partire da una nicchia</strong> e poi espandersi.</p>
</li>
<li>
<p><strong>L'importanza del cofounder.</strong> Nel libro c'è un capitolo bellissimo sul momento in cui le startup nascono, che Thiel giudica un momento <em>magico</em>. E ribadisce che <a href="https://michelenasti.com/2017/06/09/we-waglio-vuoi-venire-a-lavorare-nella-mia-startup.html">tra i cofounder ci dev'essere un patto di sangue</a>. A quest'affermazione segue il corollario:</p>
</li>
</ul>
<blockquote>
<p>Se hai un'idea, e all'atto della creazione dell'impresa non hai TUTTE le competenze per poterla sviluppare indipendentemente, <strong>LASCIA PERDERE</strong>. Eviterai di farti male.</p>
</blockquote>
<h2>Ma quando la fondi la tua startup?</h2>
<p>E' ancora presto: non so ancora qual è il problema che vorrei risolvere, come farci i soldi, ma almeno nel frattempo mi sto facendo una cultura ;)</p>
<p>C'è un'opinione diffusa che tutti gli informatici dovrebbero diventare miliardari perchè basta una buona idea e il gioco è fatto, ma tanti informatici poveri dimostrano che non è così.</p>
<p>Una volta ho letto un libro sull'<em>arte moderna</em>, <a href="https://www.amazon.it/potevo-anchio-Perch%C3%A9-contemporanea-davvero/dp/8804585579/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=ilblodimicnas-21&linkId=87fce620f2dee7dd9857097b25f37a86">"Lo potevo fare anch'io"</a>. Ditemi la verità, quante volte l'avrete pensato guardando i quadri moderni? Il punto però è che non l'avete fatto. Non avete mai neanche provato a realizzare un quadro, e vi scandalizzate se ora valgono milioni. Ecco, <strong>con l'IT succede più o meno la stessa cosa</strong>: ricordo quando da studente universitario mi lanciavo in dissertazioni coi colleghi, suggerendo che avremmo dovuto copiare questo & quello perchè ci voleva poco - la classica sindrome del "lo potevo fare anch'io". Ma poi non l'abbiamo fatto.</p>
<p>Dopo aver letto alcuni libri sull'argomento ho francamente capito che, oltre ad avere un prodotto che spicca, una startup deve anche investire molto sul marketing e deve essere capace di trovare subito il modo di incamerare utenti & soldi, altrimenti... la fine è più vicina del previsto.</p>
Guida alla Val d'Aosta con un neonato
2017-08-06T15:11:47Z
https://michelenasti.com/2017/08/06/vacanze-guida-alla-val-d-aosta-con-un-neonato.html
<p>Questo post vuole provare ad essere sintetico, e contemporaneamente raccontare 7 giorni di vacanza in Val D'Aosta con una neonata (mia figlia aveva 8 mesi quando abbiamo fatto questo viaggio).</p>
<h2>Premessa: perchè Val D'Aosta</h2>
<p>Se come me odiate gli ombrelloni, la sabbia, la spiaggia, la crema solare etc etc, l'Italia è piena di posti fantastici in montagna: dal Trentino agli Appennini, ci sono tanti posti a quota almeno 1000 metri che meritano una visita. Sia perchè fa più fresco (abbiamo fatto questo viaggio quando l'Italia è stata attraversata da uno dei peggiori caldi di sempre) sia per godere dei bei panorami che solo la montagna sa offrire.</p>
<h2>I neonati possono superare i 1000 metri?</h2>
<p>Considerato che, essendo campani, una settimana al mare la dobbiamo fare per forza (altrimenti i nonni ci cancellano dall'eredità), un'altra settimana ce la siamo concessa per noi. I motivi che ci hanno spinto fino a qui:</p>
<ul>
<li>anche se la piccola è davvero molto piccola, e sta mangiando ancora pappette e omogeneizzati, mia moglie ha pensato che a 8 mesi il viaggio era "fattibile". Senza di lei non avrei saputo come muovermi. C'è tutto quell'universo fatto di pannolini e pastine in cui non mi sento completamente a mio agio. Se vostra moglie non è d'accordo, non organizzate nulla :)</li>
<li>8 mesi credo sia stata l'età minima per venire fin qui: se avete un bimbo più piccolo, non è cosa. La bimba a 8 mesi è sufficientemente sveglia da godersi le passeggiate (cascate, boschi, montagne, laghi...) e da stare seduta senza problemi nello zaino porta neonati.</li>
<li>Abbiamo consultato la pediatra prima di partire. La domanda che ci siamo fatti è stata proprio questa: i neonati fino a che quota possono arrivare? On line abbiamo letto di non superare i 2000, e la nostra pediatra ce l'ha confermato. Anzi in realtà lei era anche più restrittiva, nel senso di non andare oltre i 1500-1600. Il problema non è neanche la quota (finchè si sta sotto i 2000 non si dovrebbe rischiare molto, ma ogni bambino è una storia a se) quanto il dislivello. Vivere a 1600 e poi arrivare a 1800 non è un dramma, mentre vivere sul livello del mare e trovare all'improvviso a 2000 potrebbe portare a difficoltà respiratorie. <strong>Consigliate anche voi un pediatra prima di fare questo tipo di passeggiate.</strong> Nel nostro caso, non abbiamo mai superato i 2000 metri.</li>
</ul>
<h2>Come siamo arrivati fin qui</h2>
<p>Mia moglie non ama i viaggi in macchina troppo lunghi, specialmente perchè la bimba sta seduta dietro con lei. Quindi piuttosto che farci 14 ore di auto <strong>abbiamo preferito volare fino a Bergamo e fittare un'auto</strong>. Se scegliete questa strada, alcune compagnie (es. Ryanair) vi faranno portare 2 bagagli in più per i neonati (noi abbiamo portato il sediolino auto e il passeggino).</p>
<p>L'auto l'abbiamo fittata online su uno dei tanti portali. Prendete qualcosa di più grande di una Panda, visto che caricherete l'auto di valigie e persone.</p>
<h2>Cos'altro serve per godersi la vacanza</h2>
<p>Ammetto di averci ragionato su solo dopo aver prenotato: <strong>come faccio a portarmi una bimba in giro per i sentieri?</strong> In braccio non è neanche un'ipotesi! Per cui, mi sono connesso al web e ho scoperto che <strong>esistono gli zaini porta neonati</strong>: il neonato sta seduto dietro la schiena del papà, ed è addirittura felice. Ha l'occasione di guardare il panorama senza sforzare collo o altro, inoltre è completamente assicurato allo zaino, non rischiando di cadere.</p>
<p>Gli zaini sono normalmente composti da parasole e kit anti pioggia, quindi anche le condizioni più estreme sono affrontabili (ma voi non uscite quando piove, right?)</p>
<p>Ricordatevi che <strong>in montagna il meteo cambia facilmente</strong>, quindi portatevi un maglione (io non l'ho mai messo) e/o una giacca a vento con cappuccio (neanche questa mai usata). A 2000 mt il vento può essere gelido. Nostra figlia era sempre a maniche lunghe e pantalone lungo, oltre al cappellino.</p>
<h2>Dove abbiamo alloggiato</h2>
<p>Ho preso un <strong>appartamentino su AirBnb a Morgex</strong>, vicino Courmayeur. L'ho scelto un po' a caso, sarò sincero; tuttavia non ci siamo trovati male. La mia idea era di andare a Courmayeur, ma siccome le case costavano troppo, ho scelto quella che costava meno e che stava più vicina. Tanto, avendo la macchina, potevo muovermi in libertà (e così è stato).</p>
<p>Se dovessi tornare probabilmente sceglierei altri due posti:</p>
<ul>
<li><strong>Cogne</strong>, con i suoi prati sconfinati</li>
<li><strong>Aosta</strong>, per essere al centro della Valle, poter andare in giro ovunque, e godersi anche la vita serale di una città.</li>
</ul>
<p>In ogni caso la valle è "abbastanza" piccola, e i punti più remoti si raggiungono in max 1h di macchina.</p>
<h2>Le passeggiate che abbiamo fatto</h2>
<h3>Val Ferret: da Arpnuova al Rifugio Elena</h3>
<p>Essendo vicini a Courmayeur ho letto molte recensioni fantastiche su questa valle, soprattutto perchè è ancora selvaggia e ben preservata. La Val Ferret è una valle, appunto, tra catene montuose che raggiungono i 3-4000 metri. Il comune non permette alle auto di arrivare fino in fondo: potete parcheggiare nel punto più lontano possibile e poi potrete prendere l'autobus per la località Arpnuova (costo del bus 1€, passa ogni mezz'ora). Da lì potete imboccare i due sentieri per il rifugio Elena, uno sale per la strada sterrata e l'altro è più selvaggio; fatene uno all'andata e l'altro al ritorno.</p>
<p><img src="https://michelenasti.com/images/val_ferret.jpg" alt="Val Ferret " title="Val Ferret " /></p>
<p><strong>Perchè vederlo:</strong> il panorama dal rifugio è fantastico, si vede tutto il ghiacciaio del Monte Bianco.</p>
<p><strong>Difficoltà Familiare® (nuovo indice™ inventato da me, ossia quanto è difficile portarci su una bimba dietro la schiena, e la moglie)</strong>: Siccome ci sono circa 300 mt di dislivello, la strada sterrata è facile ma in costante salita. Fate molte soste.</p>
<p><strong>Bonus:</strong> mangiate la crostata al rifugio. E' buona!</p>
<h3>Bionaz: dal lago di Place Moulin al rifugio Prarayer</h3>
<p><strong>Arrivare al lago con l'auto è l'unica cosa difficile</strong>. Una volta parcheggiato (3€ tutto il giorno!) un comodissimo sentiero costeggia il lago artificiale (delimitato da una diga) e si potranno vedere fantastici panorami, oltre a cascate continue sia da un lato sia lungo il sentiero stesso. Dietro il rifugio poi c'è un fantastico prato verde e un ghiacciaio.</p>
<p><img src="https://michelenasti.com/images/bionaz.JPG" alt="" /></p>
<p><strong>Perchè vederlo:</strong> è un posto che non mi sarei aspettato di trovare.</p>
<p><strong>Difficoltà familiare:</strong> Facilissimo perchè praticamente in piano.</p>
<p><strong>Bonus:</strong> come al solito, la crostata al rifugio: fantastica.</p>
<h3>Lago d'Arpy</h3>
<p><strong>Questo sentiero è facilissimo e molto pittoresco</strong>. Si parcheggia lungo la strada vicino all'hotel Genzianella (non si paga) e si prende il sentiero che parte da lì, che è molto comodo. Il sentiero è molto bello già di suo, <strong>ma il laghetto alpino (quota 2066mt) è fantastico</strong>. In cima c'erano centinaia di famiglie con i bimbi e i cani: sembrava un concertone estivo di Cristina D'Avena. Forse la metafora non è congeniale visto che non riesce a trasmettere il senso di pace e il silenzio di cui si godeva lassù.</p>
<p><img src="https://michelenasti.com/images/arpy.JPG" alt="" /></p>
<p><strong>Perchè vederlo:</strong> un lago a 2000 metri è qualcosa di purissimo. Ci si rilassa solo a stare lì.</p>
<p><strong>Difficoltà familiare:</strong> Facile, il dislivello non è tantissimo.</p>
<p><strong>Bonus:</strong> dopo esserci goduti il lago abbiamo fatto due cavolate; la prima è che abbiamo provato a fare il giro intorno al lago (non fatelo! Vi perderete come abbiamo fatto noi!) e la seconda è che siamo saliti sul Colle della Croce per vedere il panorama del Monte Bianco. anche se il panorama è fantastico, la salita è massacrante (soprattutto se avete una bimba dietro la schiena). Salite lassù solo se non avete zaini da portare con voi.</p>
<h3>Cogne e le cascate di Lillaz</h3>
<p>Cogne è uno dei paesini più caratteristici della Val D'Aosta. La piazzetta di Cogne da direttamente sul Gran Paradiso ed è fantastica anche solo per fare le foto: ci si può rilassare anche solo guardando il prato verde.</p>
<p><img src="https://michelenasti.com/images/cogne.JPG" alt="" /></p>
<p>Oltre al centro cittadino di Cogne (parcheggio con strisce bianche in molti punti...) siamo stati anche alle cascate di Lillaz, nell'omonima frazione.</p>
<p>Il sentiero delle cascate è corto ma molto appeso: per fortuna facendo le foto a ogni cascata la stanchezza non si sente affatto. Il punto più suggestivo comunque è la cascata più grande, che sta dietro a una grossa recinzione "proprietà privata - mantenersi sul sentiero" che ci ha un po' disorientati. Una volta entrati, siamo arrivati alla cascata più grande di tutte dove abbiamo anche messo i piedi in acqua (gelida, ovviamente).</p>
<p><img src="https://michelenasti.com/images/lillaz.jpg" alt="" /></p>
<p><strong>Perchè vederlo:</strong> le cascate sono affascinanti a prescindere.</p>
<p><strong>Difficoltà familiare:</strong> "Quasi Facile". In questo sentiero mia moglie ha iniziato a sentire dolori a un ginocchio, per fortuna era uno degli ultimi giorni.</p>
<p><strong>Bonus:</strong> Entrate nella quasi-proprietà privata, superate la recinzione: dentro abbiamo trovato dei cavalli allo stato brado che stavano riposando e mangiando!</p>
<h3>E le città?</h3>
<p>Spezzate le escursioni con una visita a Courmayeur, Cogne, Aosta o comunque visitate i paesini che avete intorno: sono tutti in puro stile montano e meritano una passeggiata. In Estate ci sono centinaia di manifestazioni, concerti... Difficile annoiarsi!</p>
<p>7 giorni qui e vi ricaricate per un anno intero... almeno per me funziona così.</p>
<p>Buone vacanze!</p>
Due-tre opinioni sul Growth Hacking da uno che non ha mai fatto nulla di marketing
2017-08-18T00:00:00Z
https://michelenasti.com/2017/08/18/due-tre-opinioni-sul-growth-hacking-da-uno-che-non-ha-mai-fatto-nulla-di-marketing.html
<p>Growth Hacking. <strong>Growth Hacking ovunque.</strong> Pure nei bagni dell'autogrill.</p>
<p>E' la buzzword del momento: un fenomeno che sta riguardando l'universo del marketing e delle startup, che si propone come la Metodologia Unica™ di accrescimento della vostra società, budget permettendo.</p>
<p>Andiamo al sodo; ecco come è nato questo post: dal libro sul <a href="http://amzn.to/2wfS2LO">Growth Hacking di Luca Barboni</a>.</p>
<p><a href="http://amzn.to/2wfS2LO"><img src="https://michelenasti.com/images/growth_hacking.jpg" alt="" /></a></p>
<h2>Il libro</h2>
<p>Sono rimasto molto soddisfatto da questo libro: io sono un programmatore e non conosco nulla di marketing. Per me il marketing era tutto quello che si faceva per sponsorizzare il prodotto sui vari canali: pubblicità, testi, immagni, grafiche, video, etc. In genere c'è un "budget" e un "reparto" della tua società che deve stare attento a come lo si spende. E' questo, no?</p>
<p>Se la pensi così ti sarai accorto che c'è un problema enorme alla base: non saprai mai quale azione ha portato a un aumento del fatturato. I miei clienti hanno visto la pubblicità su facebook? Oppure quella in tv? O forse il cartellone sull'A4? Non c'è nessun modo per saperlo.</p>
<p>Il Growth Hacking invece è una disciplina empirica (col nome impronunciabile per un italiano) che permette di <strong>misurare,</strong> tramite <strong>esperimenti controllati</strong>, l'impatto di ogni tua azione sui tuoi clienti.</p>
<p>Il libro è piuttosto operativo, infatti superati i primi capitoli introduttivi troverete un'intera sezione su come mettere in pratica tutte le fasi che compongono la crescita di un prodotto: dal momento in cui un cliente vi scopre, al momento in cui acquista qualcosa da voi, fino al momento in cui vi consiglia a un amico.</p>
<h2>Cosa mi ha colpito</h2>
<p>Ci sono cose che ho annotato e che mi hanno colpito maggiormente:</p>
<ul>
<li>
<p>GH non è un insieme di tips n' tricks. E' una metodologia. Se trovate siti che vi propongono <em>cinquanta azioni di Growth Hacking che potete mettere in pratica sul vostro sito</em>, beh, scappate perchè non sanno nenache di che stanno parlando.</p>
</li>
<li>
<p><strong>NON iniziare il GH senza avere un sistema per misurare ogni possibile azione che ti interessa</strong>. Ad esempio, se vuoi sapere quanti clienti vengono sul tuo sito da facebook, devi avere modo di poterlo tracciare (landing page dedicata, etc). Se vuoi tracciare il numero di clienti che si iscrivono tramite altri clienti, DEVI avere una piattaforma che ti permette di tracciare tutto.</p>
</li>
<li>
<p>Esistono <strong>due momenti</strong> per un'azienda che sta lanciando un prodotto: la fase <strong>pre-market fit</strong> e la fase <strong>post-market fit</strong>. In pratica, nella prima fase state realizzando un prodotto e state cercando di fare in modo che sia ciò di cui il cliente ha bisogno. In questa prima fase vi focalizzate sulle funzionalità più che sulla sua crescita. La seconda fase invece corrisponde a quando avrete creato un prodotto "valido", e potete finalmente dare fuoco al budget per pubblicizzarlo.</p>
</li>
<li>
<p>Il marketing non è qualcosa di esterno all'azienda, non è un ufficio a parte: <strong>il primo growth hacker è il CEO</strong>. Tutti dovrebbero sapere che esperimenti stiamo facendo, cosa ha funzionato, e cosa no.</p>
</li>
</ul>
<h2>Ma la cosa che più mi ha sorpreso è che ...</h2>
<p>Non essendo un marketer non posso confermare nè smentire: <strong>davvero nel marketing "classico" i soldi vengono spesi "alla cieca" senza nessun controllo sul risultato ottenuto?</strong> Davvero non si misura nulla, se non il fatturato o il numero di clienti, in termini piuttosto generici? Su questo sono sconvolto: sarà che vengo dall'informatica, ma penso che se non ti poni obiettivi misurabili puoi dire tutto e il contrario di tutto, senza avere mai ragione.</p>
<p>Infine, mi sono posto una domanda che - sono sicuro - si saranno posti tutti: <strong>si può fare Growth Hacking per aziende tradizionali?</strong> Io intendo per aziende che hanno un business decisamente offline, non so, pensate a una pizzeria o a un negozio di vestiti.</p>
<p><strong>La risposta del Growth Hacking "classico</strong>": "si può fare, basta che la parte di promozione la fai online e continui a misurare tutte le azioni e reazioni".</p>
<p><strong>La risposta di Michele Nasti™</strong>: Si può fare anche se non si ha un sito internet, nè una presenza online consolidata: la metodologia è applicabile anche "fuori da internet" (è una metodologia!) e la parte di metriche può essere raccolta offline... e i calcoli si possono fare "a mano"... etc etc insomma è chiaro che diventa più complicato e, presumibilmente, impreciso, ma si può fare. Forse a questo step non ci siamo ancora arrivati, e servono ulteriori semplificazioni per migliorare il processo. Ma in cuor mio penso che si possa fare. L'unico vero problema è che <strong>non è scalabile</strong>: se il negozio è grande, o è molto trafficato, diventa difficile tracciare gli eventi che ci interessano.</p>
<p>Bene, dopo tutto sto casino non mi resta che augurarvi un <strong>crescita iperbolica</strong>! Se diventate ricchi, ricordatevi di me.</p>
Come capire il bitcoin: le funzioni crittografiche
2017-09-19T00:00:00Z
https://michelenasti.com/2017/09/19/come-capire-il-bitcoin-le-funzioni-crittografiche.html
<p>Ormai sono un paio di mesi che mi sono appassionato all'anarchico mondo del bitcoin, ossia questa moneta virtuale che esiste su internet che gode di un'infinita serie di proprietà positive: è anonima, decentralizzata, incontrollabile, incopiabile, incraccabile...</p>
<p>Finchè si trattava di scimmiottare gli agenti di borsa era anche divertente, ma da quando abbiamo tenuto il <a href="https://www.meetup.com/it-IT/preview/devday-salerno/events/242583299">primo meetup a Salerno sul Bitcoin</a> ho capito che bisognava assolutamente provare a capirne di più.</p>
<p>E allora mi sono iscritto a <a href="https://www.coursera.org/learn/cryptocurrency/">questo corso on line</a> che frequento al mattino presto e che sta rispolverando concetti che avevo "quasi" dimenticato - anni fa ho sostenuto l'esame di sicurezza su reti e da allora ho promesso a me stesso che non avrei mai più fatto sicurezza, com'è bello quando la vita ti contraddice (e mi è capitato già un'altra decina di volte, pensate voi...)</p>
<p>In questo articolo vi parlo di un concetto chiave alla base di bitcoin, blockchain e tutto il resto appresso: le funzioni Hash.</p>
<h3>le funzioni Hash</h3>
<p>Hash in italiano significa <em>carne tritata</em>, ma onestamente non ho mai visto un inglese fare le polpette quindi dobbiamo fidarci di google. Tuttavia carne tritata gli si addice: in effetti quello che fa una funzione hash è proprio di prendere un messaggio (pensate a una stringa, ma in realtà può essere qualunque oggetto), e calcolarne un valore di lunghezza fissa. Vediamone un esempio, mi appoggio a NodeJS per comodità e perchè è uno dei più immediati, ma volendo potete giocare con qualsiasi linguaggio perchè le funzioni di crittografia sono native in ogni ambiente.</p>
<p>Creiamo una directory di esempio e installiamo il modulo <code>crypto-js</code>:</p>
<pre><code>$ mkdir esempi-crypto
$ cd esempi-crypto
$ npm i -S crypto-js
</code></pre>
<p>Forse l'environment vi darà degli errori che manca il file <code>package.json</code>, ma noi freghiamocene.</p>
<p>Ora siamo pronti a sbizzarrirci. Da console apriamo l'environment invocando il comando <code>node</code>:</p>
<pre><code>$ node
> let CryptoJS = require('crypto-js')
undefined
</code></pre>
<p>Abbiamo appena importato la libreria che contiene le funzioni crittografiche più diffuse. Non preoccupatevi per quell'undefined.</p>
<p>E' il momento di provare a fare l'hash di un messaggio. L'algoritmo più usato e considerato più sicuro è SHA256, ossia prende qualunque messaggio e lo trasforma in una stringa di 256 bits. Proviamolo:</p>
<pre><code>> let message = "This is my password"
> let sha = CryptoJS.SHA256(message)
> console.log(sha)
{ words:
[ -355107296,
1827616648,
440617958,
157501257,
219431749,
-1604612205,
-2070175323,
1954178935 ],
sigBytes: 32 }
undefined
> console.log(sha.toString())
ead57e206cef37881a434be6096347490d144345a05b8f93849ba1a5747a6777
</code></pre>
<p>Cosa è accaduto nel primo caso? <code>sha</code> è un oggetto contenente dei byte (tutta sta roba, a livello crittografico, opera su byte e non vorrei sconvolgervi ma SI, si può fare anche in nodejs e javascript in generale)</p>
<p>Però dei byte non ce ne facciamo niente, siamo pur sempre esseri umani e se vogliamo vedere <code>sha</code> come stringa basta chiamare il metodo <code>toString()</code> che ci restituisce lo stringone che vedete in fondo.</p>
<h3>"A me sembra solo una stringa randomica..."</h3>
<p>Hai ragione a pensarlo, caro lettore, però non è così. Le funzioni hash (e quindi anche SHA256 che abbiamo usato, che è una delle tante funzioni hash) garantiscono le seguenti proprietà:</p>
<ul>
<li>
<p>Una funzione hash prende ogni possibile stringa come input, e restituisce una stringa di dimensioni fisse.</p>
</li>
<li>
<p>la funzione deve essere computata efficientemente: nell'esempio precedente accade tutto in pochissimi microsecondi.</p>
</li>
<li>
<p>la funzione è <a href="https://en.wikipedia.org/wiki/Collision_resistance">collision-resistant</a>: è impossibile (per essere precisi, è difficile farlo in tempi ragionevoli) per chiunque trovare due stringhe diverse <code>x</code>e <code>y</code> che restituiscano lo stesso hash (matematicamente: <code>H(x) = H(y)</code>).</p>
</li>
<li>
<p>la funzione hash <em>nasconde</em> il messaggio originario: se vi do l'hash di un messaggio H(x), è impossibile risalire al messaggio. Ciò è particolarmente vero se concateniamo una stringa casuale prima del messaggio (chiamata <em>salt</em>), che noi potremmo definire come una "chiave" , per intenderci.</p>
</li>
<li>
<p>la funzione hash è <em>puzzle-friendly</em>: per ogni possibile hash, se il salt è scelto davvero a caso, è infattibile risalire al messaggio se non cercando nell'insieme di tutte le chiavi possibili. L'idea quindi è di fornire un <em>puzzle</em> di questo tipo: dato un salt (casuale) e un insieme finito di soluzioni Y, trovare un messaggio tale che la chiave, concatenata al messaggio, appartenga all'insieme delle soluzioni. Per risolvere questo puzzle bisognerà scorrere tutti i possibili messaggi.</p>
</li>
</ul>
<p>Bel casino! Scommetto che la terza proprietà vi ha complicato la comprensione.</p>
<p>Ma internet è pieno di risorse e ho trovato questa immagine che spiega molte cose. Il bitcoin si affida proprio a questo genere di puzzle per il mining. Nel primo esempio, data una chiave <code>Cheescake</code>, trovare un hash che inizi con la lettera <code>A</code>.</p>
<p>Nel secondo esempio, si parte con la stringa <code>Ice ice baby - too cold!</code> e bisogna trovare un hash che inizi con <code>Hi</code>.</p>
<p>Nel terzo esempio, si parte con la stringa <code>Who let them out?</code> e si cerca un hash che inizi con <code>Dog</code>.</p>
<p><img src="https://michelenasti.com/images/solvingahashpuzzle.png" alt="" /></p>
<p>Questa immagine l'ho presa dal sito <a href="https://3583bytesready.net/2016/09/06/hash-puzzes-proofs-work-bitcoin/">3583 bytes free, ready?</a> che ha fatto il lavoro sporco per me.</p>
<p>Come possiamo vedere, per risolvere il primo puzzle ci sono voluti 15 tentativi, per il secondo 1797 e per risovlere il terzo ben 773177.</p>
<p>Questo gioco non è altro che il <strong>mining</strong>: a partire dall'hash del blocco precedente, trovare un numero che restituisca un hash che inizia con ... qualcosa, poi vedremo cosa. Questo problema non è facile e richiede milioni di milioni di calcoli, motivo per cui si ricevono dei bitcoin come premio.</p>
<p>Non mi resta che salutarvi con l'hash di <code>Buona giornata</code>:</p>
<pre><code>9b1ec4ffe99ed6f9c898e91d79d197a7399519c53b3b0671d4e34664d6dcae21
</code></pre>
<p>Alla prossima!</p>
JWT: what is it? How does it work? Why should we use it?
2017-09-21T00:00:00Z
https://michelenasti.com/2017/09/21/a-clear-explanation-of-jwt.html
<p>In this post I'm going to talk about an authentication "protocol" called JWT, that allows to secure an API so that only authenticated users can use some requests of your API. And more importantly, a user cannot impersonate another by changing something in the request, so we can be reasonably certain that a user is who claims to be.</p>
<p>However, to understand JWT, we must step back and talk about hashing functions.</p>
<p><!--more--></p>
<h2>Hashing: a one-way transformation function</h2>
<p>JWT is universal, it will be easy to grasp the concepts in any language you like, but I'll use NodeJS for this post.</p>
<p>Create a new Node project anywhere you like. We will use this project to do our experiments. In order to use hashing functions in Node, create and enter a new directory, and install <code>crypto-js</code>:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">npm</span> i <span class="token parameter variable">--save</span> crypto-js</code></pre>
<p>Now, let's create a new file, <code>hashing.js</code> and fill it with this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span><span class="token constant">SHA256</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'crypto-js'</span><span class="token punctuation">)</span><br /><br /><span class="token keyword">let</span> message <span class="token operator">=</span> <span class="token string">'I am user number 3'</span><br /><br /><span class="token keyword">let</span> hash <span class="token operator">=</span> <span class="token constant">SHA256</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Message: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>message<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hash: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>hash<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span></code></pre>
<p>Let's run this file. What will we get?</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> hashing.js <br />Message: I am user number <span class="token number">3</span><br />Hash: 9da4d19e100809d42da806c2b7df5cf37e72623d42f1669eb112e23f5c9d45a3</code></pre>
<p>What's happening here? We are importing the function <code>SHA256</code>, that is one of the strongest hash functions we know for the moment, with mathematical proofs, etc. 256 is the number of bits of the hashed message.</p>
<p>Basically, the <code>message</code> text will be converted in a new string of just 256 bits, and this string has some unique properties:</p>
<ul>
<li>
<p>the same message will always receive the same hash.</p>
</li>
<li>
<p>If you change just one character from the <code>message</code> string, you get a completely different hash.</p>
</li>
<li>
<p>The probability that two different strings get the same hash is definitely low.</p>
</li>
<li>
<p>It's impossible to get the original message back.</p>
</li>
</ul>
<p>Curious? I won't lie to you, security is one of the hottest themes in computer science; this stuff is hard, btw we are just using it and the three points before should be sufficient for you to understand.</p>
<h2>When to use a hash</h2>
<p>You might use Hashes in a lot of different situations; we will use them in <strong>authentication</strong>, but another famous use case is to store passwords in your database. <a href="https://michelenasti.com/2017/09/19/come-capire-il-bitcoin-le-funzioni-crittografiche.html">Bitcoin also uses hashes to mine new bitcoins (italian)</a>.</p>
<p>Passwords should never be stored as plaintext, because users have this bad habit to use the same password everywhere (I guess you don't, do you?).</p>
<p>So, when a user logs in your system, you should hash their password, and see if it matches the one stored on the db.</p>
<p>To increase security, always use HTTPS too.</p>
<h3>Back to our hashes</h3>
<p>So the user has logged in. Now we want to provide them with a token that certifies that they are who they say they are. What can we do?</p>
<p>It's not so safe to send the password over and over; an attacker could steal it by listening to the network calls (man in the middle attacks) or by just hacking the client, or the server.</p>
<p>The safest way is to provide them with a <strong>token</strong>. A token is a piece of data (a json, obviously) with some authentication data we need. We give this token to the client, and he will send it back to us with every request.</p>
<p>So let's create a json object with the user' ID:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">4</span><br /><span class="token punctuation">}</span></code></pre>
<p>We obviously don't want that the user can change their ID with some other ID, for example the admin ID. So we can create a new object called <code>token</code> that contains the data and the hash of the data:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">4</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> token <span class="token operator">=</span> <span class="token punctuation">{</span><br /> data<span class="token punctuation">,</span> <br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token constant">SHA256</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>We now have a token, but ... if the user tamper the data, he can just recalculate the hash and he won.</p>
<h3>Salting the HASH</h3>
<p>How can we prevent that the user won't tamper the data? The most used technique it's also the simplest: we just concatenate a string, called <strong>salt</strong>, that in our case is a secret password.</p>
<p>So the previous example would be something like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> token <span class="token operator">=</span> <span class="token punctuation">{</span><br /> data<span class="token punctuation">,</span> <br /> <span class="token literal-property property">hash</span><span class="token operator">:</span> <span class="token constant">SHA256</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'somesecret'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>Wait, but what if the user knows the salt? <strong>They shouldn't!</strong> Once they're logged in, you send them this <em>token</em> signed with the <em>server's salt</em> and you can be sure that the client cannot recreate the hash back again.</p>
<h2>Don't reinvent the wheel: Json Web Token</h2>
<p>We could write all the edge cases, but that would be a lot of work. Instead, there's a library that does just this, <strong>JSON Web Token</strong> (JWT): it has becomed a de-facto standard for authentication tokens, even if not all the web loves it.</p>
<p>Let's install it:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">npm</span> i <span class="token parameter variable">--save</span> jsonwebtoken </code></pre>
<p>With this package we get two functions: one to <strong>create</strong> the token (<code>sign</code>), and another to <strong>verify</strong> (<code>verify</code>).</p>
<p>So let's create a new file (we are not going to need the previous stuff anymore) and start over.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'jsonwebtoken'</span><span class="token punctuation">)</span><br /><br /><span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">10</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> secret <span class="token operator">=</span> <span class="token string">'123abc'</span><br /><span class="token keyword">let</span> token <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">sign</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> secret<span class="token punctuation">)</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>token<span class="token punctuation">)</span></code></pre>
<p>And this becomes:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> jwtExample.js <br />eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsImlhdCI6MTUwNTk4MjUxOX0.w<br />DMat521KBdCoTj0XiqYcHxLkTblN01C9v8y26pEtl8</code></pre>
<p>this is different from the hash we presented before: it is divided in three blocks, and it's clearly bigger. What's going on?</p>
<p>Let's copy this string and go over <a href="http://jwt.io/">jwt.io</a>, where you'll see a cool tool that will explain the JWT: paste it in the left side.</p>
<ul>
<li>
<p>We see the <strong>header</strong>, that contains the algorithm used the type of token issued;</p>
</li>
<li>
<p>we see the <strong>payload</strong>, that contains our original data along with a new property called <code>iat</code>(<em>Issued at</em>, the time this token was created);</p>
</li>
<li>
<p>and finally we see the <strong>verify signature</strong> that will allow us to inser the secret and verify that the token is right. Try to insert our <code>secret</code> (<code>123abc</code>) and the verification box will change to <em>Signature Verified</em> ;)</p>
</li>
</ul>
<p><img src="https://michelenasti.com/images/jwt.PNG" alt="screenshot of JWT website" /></p>
<h3>Verify that the token is valid</h3>
<p>Very very easy:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//add this to the preceeding script </span><br /><br /><span class="token keyword">let</span> decoded <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>token<span class="token punctuation">,</span> secret<span class="token punctuation">)</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'decoded'</span><span class="token punctuation">,</span> decoded<span class="token punctuation">)</span><br /></code></pre>
<p>output:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> jwtExample.js <br />eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsImlhdCI6MTUwNTk4MjUxOX0.w<br />DMat521KBdCoTj0XiqYcHxLkTblN01C9v8y26pEtl8<br />decoded <span class="token punctuation">{</span> id: <span class="token number">10</span>, iat: <span class="token number">1505982519</span> <span class="token punctuation">}</span><br /></code></pre>
<p>And if we try to change anything in the token, or in the secret, we get a javascript Error back:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//change the secret to fail the validation </span><br /><br /><span class="token keyword">let</span> decoded <span class="token operator">=</span> jwt<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>token<span class="token punctuation">,</span> secret<span class="token operator">+</span><span class="token string">'blah'</span><span class="token punctuation">)</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'decoded'</span><span class="token punctuation">,</span> decoded<span class="token punctuation">)</span><br /></code></pre>
<p>output:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">node</span> jwtExample.js <br />eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsImlhdCI6MTUwNTk4MjUxOX0.w<br />DMat521KBdCoTj0XiqYcHxLkTblN01C9v8y26pEtl8<br /><br />/mnt/c/Users/miche/Documents/code/node-udemy/node-todo-api/node_modules/js<br />onwebtoken/verify.js:32<br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> throw err<span class="token punctuation">;</span><br /> ^<br />Error<br /> at Object.module.exports <span class="token punctuation">[</span>as verify<span class="token punctuation">]</span> <span class="token punctuation">(</span>/mnt/c/Users/miche/Documents/cod<br />e/node-udemy/node-todo-api/node_modules/jsonwebtoken/verify.js:107:17<span class="token punctuation">)</span><br /> at Object.<span class="token operator"><</span>anonymous<span class="token operator">></span> <span class="token punctuation">(</span>/mnt/c/Users/miche/Documents/code/node-udemy/no<br />de-todo-api/playground/hashingJWT.js:11:19<span class="token punctuation">)</span><br /> at Module._compile <span class="token punctuation">(</span>module.js:573:30<span class="token punctuation">)</span><br /> at Object.Module._extensions<span class="token punctuation">..</span>js <span class="token punctuation">(</span>module.js:584:10<span class="token punctuation">)</span><br /> at Module.load <span class="token punctuation">(</span>module.js:507:32<span class="token punctuation">)</span><br /> at tryModuleLoad <span class="token punctuation">(</span>module.js:470:12<span class="token punctuation">)</span><br /> at Function.Module._load <span class="token punctuation">(</span>module.js:462:3<span class="token punctuation">)</span><br /> at Function.Module.runMain <span class="token punctuation">(</span>module.js:609:10<span class="token punctuation">)</span><br /> at startup <span class="token punctuation">(</span>bootstrap_node.js:158:16<span class="token punctuation">)</span><br /> at bootstrap_node.js:598:3<br /></code></pre>
<p>Not very clear, but once you know, you'll catch it.</p>
<h3>Conclusions</h3>
<p>In the past years I have dealt with authentication checking <em>on database</em> if the user was logged.</p>
<p>Think about it: for. every. request. How much delay we introduced? Could we scale? Obviously not!</p>
<p>With JWT the database is not needed anymore (at every request), because we can stay safe that the user has not tampered data and because we know when the token was issued, so we can decide to block the authentication after, you say, 24 hours.</p>
<blockquote>
<p><strong>UPDATE</strong>: it seems that you might need the database however, with different strategies depending on your needs. Check out the comments thread, and thanks to <a href="http://disq.us/p/1mremix">Stas Kaufman</a> for pointing this to me. However, i believe JWT can scale better than session tokens: think of a 2 million users logged in to a cluster of servers. You loose some things but you earn scalability.</p>
</blockquote>
<p>Now you can handle as many users you can, thanks to hash functions and JWT!</p>
<p><em>This post would have never existed without the brilliant course by <a href="https://www.udemy.com/the-complete-nodejs-developer-course-2/">Andrew Mead about NodeJs</a>. Check it out.</em></p>
Viviamo tutti in una grande simulazione aliena? Sembra di no
2017-10-03T00:00:00Z
https://michelenasti.com/2017/10/03/viviamo-tutti-in-una-grande-simulazione-aliena-sembra-di-no.html
<p>Esitono persone che ritengono che <a href="http://www.lescienze.it/news/2016/04/16/news/universo_simulazione_computer-3054787/">viviamo in una grande, enorme simulazione al computer</a>, magari realizzata da una civiltà aliena che ci sfrutta o ci osserva, manco fossimo al Grande Fratello.</p>
<p><!--more--></p>
<p>Se non avete ancora chiuso il browser, sappiate che tra i sostenitori di questa teoria ci sono principalmente scienziati e tecnologhi di tutti rispetto, come <a href="https://www.theguardian.com/technology/2017/apr/22/what-if-were-living-in-a-computer-simulation-the-matrix-elon-musk">Stephen Hawking e Elon Musk</a>.</p>
<h3>Perché credere alla simulazione...</h3>
<p>non esistono prove, ma supposizioni e indizi. Ad esempio, il fatto che l'universo sembra avere un inizio temporale e dei confini fisici ben precisi. Il fatto che gli atomi si possano dividere in Quark, ma fino a un certo punto: poi sono indivisibili. Quindi, questo fatto che il mondo analogico avesse dei limiti discreti era la prima causa di sospetto per la frangia più <em>sci-fi</em> della scienza.</p>
<p><img src="https://michelenasti.com/images/Rick-And-Morty-Stagione-3.jpg" alt="" /></p>
<h3>...Ma esistono prove che NON viviamo in una simulazione</h3>
<p>Alcuni scienziati hanno recentemente realizzato uno studio su un esperimento non riuscito, e un interessante corollario di questo fallimento sembra proprio essere che <strong>l'universo non é una simulazione.</strong></p>
<p>A metterlo nero su bianco sono due scienziati, <a href="https://cosmosmagazine.com/physics/physicists-find-we-re-not-living-in-a-computer-simulation">Zohar Ringel e Dmitry Kovrizhi</a>, che hanno provato a simulare un particolare effetto elettrico chiamato <em>Quantum Hall</em> che rileva anomalie nello spazio-tempo, tramite un metodo analitico chiamato <em>Quantum Monte Carlo</em>.</p>
<p>Senza scendere nei dettagli, che richiedono più di un dottorato in fisica quantistica, l'idea è questa: se esiste un fenomeno in natura che obbedisce a delle leggi, che noi abbiamo "più o meno" capito, potremmo inserire queste leggi in un computer e simularlo. E' come programmare al pc le leggi che governano il moto di un'auto, o il calcio di un giocatore di PES a un pallone.</p>
<p>La "scoperta", per questi scienziati, è che questo particolare fenomeno sembra essere irrealizzabile all'interno di un computer. In particolare, se ne riescono a calcolare gli effetti finchè gli elettroni (e gli atomi) in gioco sono pochissimi, ma non appena diventano qualcosa come 2-300 (stiamo parlando di elettroni!) un computer richiederebbe tempo esponenziale per calcolare un risultato. E come disse una volta un mio prof, 2^300 (<em>due alla duecento</em>) è più o meno il numero di atomi presenti nell'universo!</p>
<h2>E se si fossero sbagliati?</h2>
<p>Ci sono ancora troppe variabili in gioco per poter dire che la questione sia conclusa. Alcune delle domande a cui onestamente non sappiamo dare una risposta:</p>
<ul>
<li>E se gli scienziati avessero sbagliato l'algoritmo, o i calcoli?</li>
<li>E se la simulazione non fosse possibile con le tecnologie attuali, ma lo sarà con quelle del futuro?</li>
<li>E se gli alieni che hanno programmato la nostra simulazione avessero deliberatamente inserito dei "glitch" all'interno del nostro universo per non farci andare troppo oltre?</li>
<li>Che senso ha vivere (e morire) se siamo in una simulazione che dura da 13.82 miliardi di anni?</li>
<li>Gli universi paralleli sono altre simulazioni lanciate in contemporanea alla nostra, ma con dati iniziali diversi?</li>
<li><strong>Le bollette, le rate del finanziamento e il mutuo sono reali?</strong></li>
</ul>
Windows non fa più così schifo: recensione Dell XPS 15" 9560 (2017)
2017-10-03T00:00:00Z
https://michelenasti.com/2017/10/03/recensione-dell-xps-15-2017.html
<p>Appena ho finito l'università ho acquistato un Macbook Air, che è durato ben 5 anni (!). All'epoca lo presi perchè, da neolaureato, non volevo chiudermi l'opzione di realizzare app per iOS, cosa che poi non è mai avvenuta. Quindi per il lavoro di tutti i giorni ho deciso di uscire dalla comfort zone e prendere un nuovo pc: un <strong><a href="http://www.dell.com/it/p/xps-15-9560-laptop/pd?oc=cnx95604&model_id=xps-15-9560-laptop">Dell XPS 9560</a>, con schermo da 15"</strong>.</p>
<h2>I mac non sono meglio?</h2>
<p>Per una grande parte degli utenti, probabilmente si. Un mac ha:</p>
<ul>
<li>un ottimo sistema operativo</li>
<li>un ottimo hardware</li>
<li>un'eccellente simbiosi hardware/software, che fa davvero la differenza</li>
<li>è facile da usare, se appartenete alla fascia di utenti normali (io no)</li>
<li>non viene colpito da virus (<em>chiarimento per i secur-nazi: i mac hanno lo 0,1% dei virus di Windows e a meno che tu non sia proprio un imbecille, è difficile che un virus possa fare troppi danni</em>)</li>
<li>ha tanti software e applicativi multimediali, alcuni eccellenti e addirittura gratuiti!</li>
<li>Hanno un terminale unix-like quindi non bisogna imparare nuovvi comandi.</li>
</ul>
<h3>Mac anche no!</h3>
<p>I Mac tuttavia hanno una serie di problemi:</p>
<ul>
<li>Costano. Almeno 1000€ in più di un equivalente Windows, o Linux.</li>
<li>Ti fanno credere di vivere in un mondo fantastico dove tutto è bello e perfetto, in cui c'è sempre una <em>i</em> davanti a tutto (_i_Tutto) e non esiste nient'altro.</li>
<li>E' compatibile solo con roba certificata per Apple, generalmente venduta da Apple, al doppio-triplo del prezzo equivalente. (Adattatori hdmi, sto parlando di voi!)</li>
</ul>
<p>Se avete i soldi, e siete consapevoli che potreste entrare in una bolla da cui sarà problematico uscire, prendete un mac e non pensateci due volte.</p>
<h2>Cosa ho scelto: Dell XPS</h2>
<p>Alla fine ho preso un Dell XPS con un monitor da 15".</p>
<p><img src="https://michelenasti.com/images/dell-xps-15-2017-nw-g01.jpg" alt="Dell XPS 15"" /></p>
<p>L'ho pagato <strong>2190€</strong>.</p>
<blockquote>
<p>Alla faccia Miche', e hai detto che il Mac costa!</p>
</blockquote>
<p>L'equivalnte Mac dello stesso computer costa <em>3.399€</em>.</p>
<p>Torniamo al Dell. La versione che ho deciso di prendere ha queste caratteristiche:</p>
<ul>
<li>Ha un processore <strong>Intel i7 di settima generazione</strong>, l'ultima al momento dell'acquisto.</li>
<li><strong>16 GB di RAM</strong>, espandibili</li>
<li><strong>hard disk da 512 GB</strong>, espandibili</li>
<li><strong>monitor da 15,6"</strong>, che per un web developer fa la differenza</li>
<li>schermo <strong>touch</strong> in <strong>4k</strong>: mi sono lasciato convincere da una cosa che non mi serviva; senza questo spendereste addirittura meno.</li>
</ul>
<p>Non entro in altri dettagli perché sarebbe per gamers, e io non sono un gamer, sono un programmatore.</p>
<h2>La caratteristica che non ti aspetti: Bash su Windows</h2>
<p>Io faccio un <strong>abuso della shell</strong> e dei suoi script, e questa storia di Bash su Windows è stato uno dei motivi per cui ho deciso di passare a Win: Non è una macchina virtuale, non è un'emulazione, ma è un vero e proprio sottosistema Ubuntu che gira sul computer windows. Le chiamate unix vengono "intercettate" da Windows che le traduce in chiamate native. Questo significa che all'interno del sottosistema linux si pul accedere a tutti i file del mondo windows (è sconsigliato fare il contrario!), oltre che lanciare comandi, eseguire script, <code>sudo apt-get install git</code> e via di seguito.</p>
<p>Una delle prime cose che ho fatto infatti è stato di scaricare <a href="https://hyper.is/">Hyper.js</a> (un terminale moderno scritto in NodeJS) e <a href="https://michelenasti.com/2017/05/30/cinque-tool-per-migliorare-la-vostra-developer-experience.html">oh-my-zsh</a>, un'estensione di zsh che permette di lavorare con più facilità.</p>
<p>Su internet ci sono <a href="https://msdn.microsoft.com/it-it/commandline/wsl/install_guide">guide ovunque</a> per settare la shell di bash anche in <a href="https://code.visualstudio.com/docs/editor/integrated-terminal">Visual Studio Code</a>, Intellij, etc.</p>
<p>Microsoft, stavolta hai fatto una cosa davvero decente: <strong>esperimento riuscito</strong> 👍</p>
<h2>Il monitor touch, 4k e 15"</h2>
<p>Il monitor ha un bordo sottilissimo per cui il computer è uno dei più piccoli per la sua fascia. Il fatto che sia lucido non mi fa impazzire, un po' li odio i monitor così. Sono felice di averlo preso 15", con più pixel posso mettere più cose sullo schermo (non sono mai stato il tipo da usare più desktop).</p>
<p>I video in 4k si fanno apprezzare, anche se per ora solo su youtube si trova roba decente.</p>
<p>Toccare lo schermo cambia proprio il modo di interagire col pc. Il touch è molto comodo in aereo, o in treno, o comunque nei posti dove non puoi usare nè mouse nè tastiera agilmente. Tuttavia non sono un grande utente di questa feature, almeno non quanto mia figlia che si diverte a toccarmi lo schermo mentre lavoro (chiude programmi, manda mail, tutto senza saper neanche camminare!).</p>
<h2>La batteria</h2>
<p>La batteria non mi ha sorpreso: dura 6 ore. Aumentando la luminosità del monitor anche meno.</p>
<h2>Connettività</h2>
<p>In un mondo dominato da porte USB tradizionali, abbandonare tutto per le sole USB-C mi sembrava un passo troppo estremo. Un mouse da 9,90€ non lo avrei più potuto collegare! Invece il Dell è dotato di due porte USB tradizionali, una USB-C, una porta HDMI, alimentatore e slot per SD. Niente più adattatori...</p>
<p>(In realtà uno l'ho dovuto comprare, quello per i monitor VGA. Ogni tanto vado a fare delle presentazioni e voglio assicurarmi di essere compatibile con qualsiasi proiettore incontri).</p>
<h2>Prestazioni</h2>
<p>E' una bomba, non c'è niente da dire. Forse potevo spendere per avere più RAM, ma come ho scritto sopra è estendibile quindi non appena saranno pochi ne acquisterò un altro po'. Forse potevo prendere più hard disk, ma da quando esistono Google Photos, Dropbox e ho il NAS a casa non conservo più nulla sul disco del pc, a parte i programmi e i workspace di lavoro.</p>
<p>Nonostante sia così potente, <strong>c'è una cosa che va lenta ed è proprio la feature che amo di più: il terminale bash</strong>. Purtroppo, essendo emulato, noto che alcuni comandi richiedono più del consentito - un semplice <code>ls</code> o un <code>git pull</code> possono durare dei secondi, e lo si accetta come un compromesso che comunque non impatta troppo.</p>
<h2>il sistema operativo: Windows</h2>
<p>Windows 10 è migliorato tantissimo. Non uso tutte le sciccherie che sicuramente ha, ad esempio Cortana la uso solo quando voglio divertirmi, però mi piace molto com'è strutturato ora il menu (in verità preferivo quello di Windows 8) e la barra delle applicazioni.</p>
<p>Ci sono tante cose su cui bisogna ancora lavorare:</p>
<ul>
<li>coesistono applicazioni dalla grafica moderna con altre che risalgono a Win98</li>
<li>ci vogliono ancora 715 click per conoscere il proprio indirizzo IP</li>
<li>serve sempre un antivirus in funzione, e Dell mi ha regalato un anno di McAfee.</li>
<li>Trovo preinstallato OneDrive che non uso, perchè ho già Google Drive e Dropbox. Non è possibile disinstallare e neanche disabilitare completamente.</li>
<li>Inserire i caratteri <code>`</code> e <code>~</code> su questa tastiera è stato un dramma. Ho risolto con AutoHotkey.</li>
</ul>
<h2>Conclusioni</h2>
<p>Potrei parlare e scrivere ancora per un paio d'ore, ma mi fermo qui perchè spero siano trasparite alcune cose: <strong>non sono un fanboy di nessuna azienda</strong> e questa la considero una virtù, infatti valuto in maniera analitica, senza sentimenti. Inoltre <strong>valuto uno strumento in relazione all'uso che ne faccio</strong>, e infatti io sono un programmatore, scrivo di programmazione e i problemi che ho io con i computer sono profondamente diversi da quelli di ogni altra categoria lavorativa; dove possibile (come in questo caso) cerco di comprare la miglior macchina che costa meno e che mi permetterà di lavorare al 100%, senza sconti.</p>
Cryptomining and websites: how long will it be tolerated?
2017-10-24T00:00:00Z
https://michelenasti.com/2017/10/24/cryptomining-and-websites-how-long-will-be-tolerated.html
<p>A lot of stuff has happenend in the crypto-mining world: antiviruses, search engines, private miners, and more.</p>
<p><img src="https://michelenasti.com/" alt="" /></p>
<p><!--more--></p>
<h2>There are private miners out there</h2>
<p>After discovering Coinhive I found out many new websites that offered the same cryptomining services: <a href="https://www.crypto-loot.com/" title="Crypto-Loot">CryptoLoot</a>, <a href="https://jsecoin.com/">JSCoin</a>... The main differences are in the payout and in the commission they take.</p>
<p>However, I've been contacted by a facebook friend that offered me to enter an "exclusive program" with a private monero miner, untraceable by antiviruses because it's self hosted.</p>
<p>The reason is that, in his opinion, Coinhive and others lie about your true visitors and give you less in terms of cash.</p>
<p>I cannot tell you his name (private!) and I must say that I tried to enter his "programme" but at the end we didn't achieve anything. It was just too complicated.</p>
<p>If your browser consumes too much CPU maybe you've found a private monero miner.</p>
<h2>Antiruses and Extensions</h2>
<p><img src="https://michelenasti.com/images/antivirus-mining.png" alt="" /></p>
<p>I have personally tried a Firefox extension called <a href="https://addons.mozilla.org/it/firefox/addon/no-coin/">NoCoin</a> and it works for me; infact, my blog was blocked. On Chrome you might try other extensions.</p>
<p>Other friends have told me that antiviruses like Avast alert the user about the script. Some friends think that my website has been hacked :p</p>
<p>This is a great problem for website owners; if you are going to use something that is considered a threat, how can you bring users to your website?</p>
<h2>Coinhive's new project: Authedmine</h2>
<p>Coinhive tries to stop antiviruses by creating a new project called <a href="https://authedmine.com/">Authedmine</a>. The idea is simple: show a popup when users land on the website, explaining what's going on, and how to opt out. The choice will be remembered for a session.</p>
<p>I'm using this on my website, now.</p>
<h2>Mining vs Ads</h2>
<p>Many bloggers or small enterprises see in cryptomining a way to avoid ads and earn some cash from their websites: users are splitted - the enthusiasts, "finally a way to not show ads entirely!" and the enemies, "the CPU is mine and I decide what to do with it".</p>
<p>However,most users just don't give a f*ck.</p>
<h2>Will my website be blocked by Google ?</h2>
<p>I believe this. Not because it's a virus (it isn't!) but because cryptomining is eroding (might erode) some market share from advertisement.</p>
<p>Don't forget that Google is a big reseller of ads, and if they won't sell ads, their business is over.</p>
<p>They are also a big search engine and if your website doesn't show up on Google, well you just don't exist.</p>
<p>So I imagine that in the future they will block cryptomining because it's interfering with their business. They can unilaterally choose this.</p>
<p>However, There is no official position from Google about this.</p>
<h3>The best comment about my experiment</h3>
<p><img src="https://michelenasti.com/images/mining-best-comment.PNG" alt="" /></p>
<blockquote>
<p>I tried to open the page, and <strong>my macbook started screaming with pain</strong>. I have marked the url, and I have promised myself to never visit again. These things should not be done for morality issues, you're literally ruining your users' hardware. I didn't even read the whole article, I closed immediately. For me, a big NO.</p>
</blockquote>
<p>By deciding to use a cryptominer in your website, you should also think of users like him.</p>
Not-A-Number: when javascript gets crazy
2017-11-14T00:00:00Z
https://michelenasti.com/2017/11/14/not-a-number-when-javascript-gets-crazy.html
<p>Yesterday I was doing some maintenance on a corporate website when I found out that <code>parseInt()</code> returns <code>NaN</code> if the argument is, well, not a number.</p>
<p><img src="https://michelenasti.com/images/not-a-number.jpg" alt="" /></p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> <span class="token keyword">let</span> pippo <span class="token operator">=</span> <span class="token function">parseInt</span><span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">)</span><br /><span class="token number">NaN</span></code></pre>
<p><code>NaN</code> is a constant and stands for <code>Not a Number</code>. Javascript uses this for some very default cases, for example if you divide a number by a string, etc.</p>
<p>That's fair. What comes next is funny :)</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> pippo <span class="token operator">==</span> <span class="token number">NaN</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> pippo <span class="token operator">===</span> <span class="token number">NaN</span><br /><span class="token boolean">false</span></code></pre>
<p>Even if a variable is <code>NaN</code>, you cannot check with the usual operators that it is <code>NaN</code>.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> pippo <span class="token operator">==</span> pippo<br /><span class="token boolean">false</span><br /><span class="token operator">></span> pippo <span class="token operator">===</span> pippo<br /><span class="token boolean">false</span><br /><span class="token operator">></span> <span class="token number">NaN</span> <span class="token operator">==</span> <span class="token number">NaN</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> <span class="token number">NaN</span> <span class="token operator">===</span> <span class="token number">NaN</span><br /><span class="token boolean">false</span></code></pre>
<p>Things start to get crazy. As you can see, <code>NaN</code> is not even equal to itself. You cannot compare anything to <code>NaN</code>, because <code>NaN</code> is not equal to anything.</p>
<h2>sooooo... isNaN() ?</h2>
<p>There is a function called <code>isNaN()</code> and it's the only way to detect a <code>NaN</code>.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> <span class="token function">isNaN</span><span class="token punctuation">(</span>pippo<span class="token punctuation">)</span><br /><span class="token boolean">true</span><br /><span class="token operator">></span> <span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> <span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'bau bau micio micio'</span><span class="token punctuation">)</span><br /><span class="token boolean">true</span></code></pre>
<p>This function seems to resolve our problems: it detects <em>not</em>-numbers, and infact a string is considered <code>NaN</code>.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> <span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span><br /><span class="token boolean">true</span><br /><span class="token operator">></span> <span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span></code></pre>
<p>I see your face 😆 Where is the logic?</p>
<p><code>isNaN</code> tries to convert the argument to a number, using the constructor <code>Number()</code>; if the argument cannot be converted, <code>NaN</code> is returned.</p>
<p>In the case of <code>isNaN(null)</code>, this is what happens:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token comment">// Number(null) ==> 0 </span><br /><span class="token operator">></span> <span class="token boolean">false</span> </code></pre>
<p>Ha ha, <code>Number(null)</code> returns zero??? Welcome to javascript 😎 This would require a bigger explanation, however JS does a type cohercion trying to compare things. It is described <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-9.3">here</a> (warning: it's a spec, the first 10 lines are understandable, then it's a mess).</p>
<p>Of course,</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">null</span> <span class="token operator">==</span> <span class="token number">0</span><br /><span class="token operator">></span> <span class="token boolean">false</span></code></pre>
<p>🙂</p>
<p>Just to add the last bit of discoveries around <code>NaN</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">Number</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><br /><span class="token operator">></span> <span class="token number">0</span><br /><span class="token function">parseInt</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <br /><span class="token operator">></span> <span class="token number">NaN</span></code></pre>
<h2>There is another isNaN() around</h2>
<p>The Javascript <code>Number</code> object also has a <code>isNaN</code> method, that is way more conservative:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'3a'</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'abc'</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><br /><span class="token boolean">false</span><br /><span class="token operator">></span> Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token number">NaN</span><span class="token punctuation">)</span><br /><span class="token boolean">true</span></code></pre>
<p>It will return <code>true</code> only for <code>NaN</code>.</p>
<h2>Stop this! It's a mess!</h2>
<p>Ok, let's end with a song:</p>
<pre class="language-javascript"><code class="language-javascript">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token number">10</span><span class="token operator">/</span><span class="token string">'a'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token number">10</span><span class="token operator">/</span><span class="token string">'b'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token number">10</span><span class="token operator">/</span><span class="token string">'c'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token number">10</span><span class="token operator">/</span><span class="token string">'d'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token number">10</span><span class="token operator">/</span><span class="token string">'e'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token number">10</span><span class="token operator">/</span><span class="token string">'f'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token number">10</span><span class="token operator">/</span><span class="token string">'g'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token number">10</span><span class="token operator">/</span><span class="token string">'h'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">a Batman!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br /><span class="token comment">//NaNaNaNaNaNaNaNa NaNaNaNaNaNaNaNa Batman!</span></code></pre>
Other thoughts about NaN (then, stop)
2017-11-16T00:00:00Z
https://michelenasti.com/2017/11/16/why-nan.html
<p>When I posted the <a href="https://michelenasti.com/2017/11/14/not-a-number-when-javascript-gets-crazy.html" title="Not-a-Number: when Javascript gets crazy">NaN article</a> on facebook in a programmers group, I received some very bad reviews. Let's analyze them.</p>
<h2>"It is obvious!"</h2>
<p>It is obvious, but it's not common. Partly because I am a developer with some years of experience and, altough I have studied and studying a lot of programming, I have never occourred in <code>NaN</code>-at-work. You might think that this is impossible, but well, it isn't because happened to me. Maybe because I rarely do maths without checking types? Maybe because I rarely do maths in Javascript (it's a very bad idea)?</p>
<h2>Why NaN === NaN is false</h2>
<p>The fact that <code>NaN !== NaN</code> is true is obvious, after you reason about it.</p>
<p>When I thought of this the first time, I thought it's absurd, but after a while I realized that this is the only possible logic behaviour. As some have said, if I divide a number by a string I get <code>NaN</code>, and if I divide another number by another string I get another <code>NaN</code>. Are these two<code>NaN</code> equal? no. <code>NaN</code> is not even a measurable property.<code>NaN</code> is a state, and as such it cannot be compared to other<code>NaN</code>s.</p>
<p>More on this: <a href="https://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values">What is the rationale for all comparisons returning false for IEEE754 NaN values?</a></p>
<h2>Why this is not a JS-only matter</h2>
<p><code>NaN</code> has not been invented by Javascript, or by Java. It is here with us since the first processor, and it is used for a wide range of cases. Here are some:</p>
<ol>
<li>
<p>Division of zero by zero</p>
</li>
<li>
<p>Dividing an infinity by an infinity</p>
</li>
<li>
<p>Multiplication of an infinity by a zero</p>
</li>
<li>
<p>Any operation in which <em>NaN</em> is an operand</p>
</li>
<li>
<p>Converting a non-numeric string or <em>undefined</em> into a number</p>
</li>
</ol>
<p>It is described in standard IEEE 754, from 1985. In this standard you can read how integer and decimal numbers are stored inside a computer (<em>spoiler: decimal numbers in a computer are not real, continuous, neither infinite).</em> You'll read of many edge cases regarding decimal numbers, along with what should happen when you're in one of the cases described before. So, <code>NaN</code> <strong>is a convention followed by pretty much every programming language</strong>.</p>
<h2>Javascript Quirks</h2>
<p>But JS does some <em>stranger things</em>.</p>
<ul>
<li>
<p>It has two <code>isNaN()</code> functions, and you must choose the one you need carefully.</p>
</li>
<li>
<p>the standard <code>isNaN()</code> function does type cohercion, that's why <code>isNaN(null) == false</code>.</p>
</li>
</ul>
<h2>And Now?</h2>
<p>Should every developer in the world know this? Yes, but chances are that you'll never need to use this bit of information.</p>
<p>Is this a good question in a job interview? Well, I don't think so.</p>
<p>Chances are that you'll never need to do this, and even if you hit <code>NaN</code>, that's something you'll learn in two hours wikipedia's reading (or my blog, hehehe).</p>
<p>In a Job interview I would focus on what the candidate can actially do and his way of reasoning.</p>
<p>Of course, if I'm interviewing for a position that has to deal with financial or mathematical programming, knowing of <code>Infinity</code> or <code>NaN</code> (and how numbers are processed by the processor). But I wouldn't do this in Javascript, because JS has no integers.</p>
<h2>Is it dumb to explore these areas of a programming language?</h2>
<p>In my opinion, no. It's just fun, and a new way to learn something.</p>
<p>In some very bad comments (ones that started with "you are idiot") there were some insightful hints that made me follow links, read articles, study more - and write this.</p>
<p>But on the web I have received some critics like "buy a book and read it", and the first thing I thought is ... <em>haters' gonna hate</em>.</p>
<p>Others have also accused me of "plagiarism" :D</p>
<p>Honestly, this happens in every group that has more than 1-2 thousand members; people will comment out just to say that you're dumb and they're better.</p>
Growth Hacker: cos'è? Intervista a Raffaele Gaito
2017-11-18T00:00:00Z
https://michelenasti.com/2017/11/18/growth-hacker-cos-e-intervista-a-raffaele-gaito.html
<p>Conosco <a href="https://www.raffaelegaito.com/" title="Raffaele Gaito, il blog">Raffaele Gaito</a> dai tempi dell'università, e mettiamolo subito in chiaro: siamo amici 😎 Oggi dobbiamo <em>fare finta</em> che non abbiamo mai bevuto una birra insieme (mai!) e parleremo di una cosa che Raf conosce molto bene:</p>
<h2>il Growth Hacking</h2>
<p>Raffaele ha pubblicato il suo primo libro (<a href="https://www.raffaelegaito.com/growth-hacker-libro/">Growth Hacker, mindset e strumenti per far crescere il tuo business</a> - c'è un'anteprima gratuita!) e ho colto l'occasione per fargli qualche domanda.</p>
<p><a href="https://www.raffaelegaito.com/growth-hacker-libro/"><img src="https://michelenasti.com/images/growth-hacker-libro-cover-3d-medium.png" alt="Growth Hacker - il libro" /></a></p>
<p>Ma questo Growth Hacking... cos'è? E soprattutto, interessa anche ai developer? Facciamocelo raccontare dal diretto interessato!</p>
<p><strong>(M) E' utile iniziare l'intervista parlando del perchè esiste il marketing: rivolgiamoci agli sviluppatori. "Non è il linguaggio di programmazione, o i framework, a decretare il successo di un prodotto, ma il marketing che ne consegue". Se il prodotto è utile, e la gente ne ha bisogno e soprattutto lo viene a sapere, allora avrà successo. E' un'affermazione vera?</strong></p>
<p>(R) Si! Assolutamente! Non conta il linguaggio, quelle sono pippe da tecnici. Hai detto una cosa chiave: la gente deve venirlo a sapere. <em>Se fai il miglior programma del mondo, col codice più ottimizzato e col linguaggio più moderno, però lo sai solo tu e tuo cugino, quante copie ti aspetti di vendere?</em></p>
<p><strong>(Michele) Partiamo dall'inizio. Hai cofondato Mangatar, una startup che si occupava di videogames, e a un certo punto hai lasciato. I motivi sono descritti</strong> <a href="https://www.raffaelegaito.com/2016-lascio-mangatar-zona-di-comfort-crescita/"><strong>in un tuo post</strong></a><strong>, ma il TL;DR è che sentivi la necessità di passare ad altro. Già in quel post (gennaio 2016) parlavi di Growth Hacking, parola che all'epoca era sconosciuta ai più. Come hai preso la strada del GH? Quand'è che l'hai conosciuto, e ne sei stato "folgorato"?</strong></p>
<p>(Raffaele): Ho iniziato ad interessarmi al Growth Hacking più o meno 4-5 anni fa. Non ricordo esattamente quando, ma già leggevo cose in giro con molto interesse, cercando di capire se fosse la solita fuffa americana o meno.</p>
<p>Ho iniziato fin da subito a fare qualche esperimento su cose più piccole, anche perché non avevo colto in pieno il potenziale della cosa, e avevo il classico approccio di chi scopre il Growth Hacking per la prima volta: pensare che si tratti di una scorciatoia e di qualche trucchetto per il successo.</p>
<p>Ne sono rimasto folgorato perché è la perfetta intersezione di mondi diversi ai quali però ero molto legato. Riusciva a mettere nello stesso contesto le mie competenze tecniche, la mia passione per il marketing e il mio essere un uomo di business, un imprenditore.</p>
<p>Una volta che scopri sta cosa non puoi più tornare indietro.</p>
<p><img src="https://michelenasti.com/images/raf_gaito.jpg" alt="Raffaele Gaito" /></p>
<p><strong>(M) Questo è un blog di sviluppatori, gente che di marketing generalmente ne capisce (volontariamente) poco. Puoi spiegaci il GH "orientato ai developer"?</strong></p>
<p>(R) La cosa bella è che il Growth Hacking è perfetto per i programmatori e te lo dico proprio da programmatore! Per chi non lo sapesse io infatti sono laureato in informatica e per una vita ho fatto lo sviluppatore. Qualsiasi linguaggio, qualsiasi piattaforma.</p>
<p>Come dicevo all'inizio io sono rimasto folgorato dal Growth Hacking proprio perché era molto concreto, molto vicino al mindset e all'approccio di un programmatore.</p>
<p><em>Lo so bene cosa pensano i programmatori del marketing ed è quello che pensavo anche io. Tutto troppo fumoso, tutto troppo generico, il rischio fuffa è dietro l'angolo.</em> Invece il Growth Hacking arriva li con una metodologia precisa, i suoi framework, i suoi tool e i suoi modelli e tu capisci che è molto di più di "semplice marketing".</p>
<p>Uno degli aspetti fondamentali del Growth Hacking è la parte di prodotto che non viene mai sottolineata abbastanza. Molte attività di Growth Hacking si fanno quindi mettendo mano al codice e modificando il proprio prodotto per sperimentare nuove soluzioni, per adattarlo dopo la raccolta dati, per integrare modifiche suggerite dagli utenti, per fare A/B testing e così via.</p>
<p>E il prodotto è programmazione, c'è poco da fare!</p>
<p><strong>(M) Possiamo dire che gli sviluppatori partono avvantaggiati se vogliono diventare Growth Hacking?</strong></p>
<p>E' vero che il programmatore può avvicinarsi molto facilmente, perchè ci sono degli aspetti tecnici del Growth Hacking che già padroneggia, ma non possiamo dire che è la categoria ideale perchè può avvicinarsi chiunque: ho colleghi che arrivano dal marketing, o dal prodotto ...</p>
<p><strong>(M) E' in uscita</strong> <a href="https://www.raffaelegaito.com/growth-hacker-libro/"><strong>il tuo libro</strong></a><strong>, immagino sia fantastico potersi definire anche "autore di libri". In giro ce n'erano già altri (pochi in verità) sul GH, io ne ho addirittura recensito uno. Perchè dovremmo leggere il tuo? (Ovviamente lo faremo 😉)</strong></p>
<p>(R) Si ho visto che hai recensito "Growth Hacking" di Luca Barboni e Federico Simonetti. Luca è un grande amico e collega. Siamo stai i primi a portare questa disciplina in Italia e collaboriamo insieme su parecchie iniziative.</p>
<p>Detto questo, perché leggere il mio libro? Provo a darti risposte diverse in base a scenari diversi:</p>
<ul>
<li>
<p>Se è la prima cosa che leggi sul Growth Hacking, è un libro perfetto con una piccola parte teorica (molto piccola) e poi tanta ciccia. Ti consentirà di capire velocemente di cosa si tratta, ma essere anche in grado di metterlo in pratica.</p>
</li>
<li>
<p>Se hai già letto altre cose sul Growth Hacking (tipo il libro di Luca), allora meglio ancora perché il taglio del libro è quello di essere strategico/operativo per la quotidianità. Se ti interessa applicare questa metodologia sul tuo progetto o vuoi farne un lavoro questo è il libro perfetto.</p>
</li>
<li>
<p>Se se già un po' più avanti e magari hai fatto anche qualche corso (uno dei miei, ad esempio), il libro è il perfetto completamento a tutto quello che hai studiato fino ad oggi. È un manualetto da tenere sulla scrivania in ufficio e tirare fuori quando ti serve!</p>
</li>
</ul>
<p>Il libro è perfetto per le agenzie, per i freelance, per i marketer, per i growth hacker, per i product manager, per i project manager e per chiunque in azienda (la propria o quella di altri) abbia la necessità crescere.</p>
<p><strong>(M) In questi anni avrai avuto a che fare con tante realtà; come è stato recepito il GH dal management delle aziende per cui hai lavorato? E dagli altri reparti? (marketing... sviluppo...)</strong></p>
<p>(R) Lo scenario è cambiato molto negli anni. Se nelle primissime fasi la cosa veniva guardata con sospetto, ora invece è tutto molto più facile. Sono anni che spingo tantissimo sull'evangelizzazione, l'informazione e la sensibilizzazione verso questa tematica e i risultati oggi si vedono.</p>
<p>Faccio corsi nelle università, vado a parlare nelle multinazionali, ho studenti in tutta Italia, il libro è finito bestseller. Diciamo che tutte queste cose sarebbero state impossibili senza aver "seminato" a lungo negli anni precedenti.</p>
<p>Ovviamente la risposta cambia da reparto a reparto, ma l'importante è essere concreti e non esagerare mai. È chiaro che <em>se vado nel reparto marketing e gli dico "il marketing è morto"</em> difficilmente otterrò la loro fiducia e la loro attenzione. Anche perché è falso e il Growth Hacking è uno strumento in più nella loro cassetta degli attrezzi.</p>
<p>Io ho sempre usato un approccio concreto fatto di tanti esempi, tanti numeri e tanto sporcarsi le mani. E dopo un po' si convincono tutti: dal manager al marketer, passando anche per i programmatori più scettici.</p>
<p><strong>(M) Una domanda che mi è arrivata dalla community di DevDay Salerno: quanto guadagna un GH?</strong></p>
<p>(R) Direi che è una domanda che lascia il tempo che trova. E come se ti chiedessi "quanto guadagna un programmatore?". Non esiste una risposta unica perché dovrei specificare in che nazione lavora, in che linguaggio programma, in che azienda lavora e così via.</p>
<p>Allo stesso modo non esiste un modo unico di rispondere alla domanda del Growth Hacker. Per darti un'idea dell'importanza che questa figura ha attualmente sul mercato, ti posso dire che da una chiacchierata fatta ultimamente con una grossa startup americana è uscito fuori che in Silicon Valley ormai tutti vogliono solo due figure: il growth hacker e il data scientist e sono disposti a pagarli a peso d'oro. Si parlava di cifre sui <strong>200k l'anno</strong>.</p>
<p>Qui in Inghilterra dove vivo io le offerte per questa figura si aggirano sui 60-80k l'anno e se guardi sui siti specializzati vedrai che in america uno stipendio medio per una figura del genere varia dai 120k ai 160k l'anno.</p>
<p>Però, ripeto, sono numeri campati in aria che hanno poco senso estrapolati dal loro contesto. Dipende se sei freelance, se sei assunto, se lavori in agenzia e così via.</p>
<p><strong>(M) Cosa fa durante una "giornata lavorativa"?</strong></p>
<p>(R) Dipende molto dal tipo di progetto su cui sta lavorando e dalla fase in cui si trova (ricordo che il Growth Hacking è un processo). Generalmente passi un sacco di tempo sui numeri e sulla strategia, che significa molto excel, molto analytics e molto trello (o asana o altro).</p>
<p>Molto dipende anche dal fatto se lavori con un team di Growth Hacking (e allora hai un ruolo più ad alto livello) oppure se sei da solo (e gestisci sia la parte strategica che operativa). Per farti qualche esempio concreto: ci sono una serie di attività legate agli esperimenti dove si va di brainstorming e prioritizzazione; tutta una serie di attività legate alla raccolta dati dove vai dallo svolgere customer interview a raccogliere gli hard data; tantissime attività sul prodotto che, come dicevo prima, coinvolgono il team di sviluppo e così via.</p>
<p>Poi ancora, in base allo step del funnel in cui ti trovi e ai canali sui quali sei attivo potresti dover gestire campagne di advertising, realizzare landing page, produrre contenuti, sviluppare nuove feature, e chi più ne ha più ne metta.</p>
<p>Come avrai capito il Growth Hacking è un mondo vastissimo dove dentro rientrano attività appartenenti a settori molto diversi tra di loro.</p>
<p>Il cuore di tutto questo è una metodologia basata sugli esperimenti, quindi indipendentemente dal cosa fai il concetto base è: <em>il Growth Hacker nella quotidianità fa test. Un sacco di test</em>.</p>
<p><strong>(M) Come si diventa Growth Hacker?</strong></p>
<p>(R) Fino a qualche anno fa la situazione era tragica perché i contenuti in italiano erano pochi e anche di pessima qualità. Quindi bisognava per forza studiare dal materiale americano e poi andare per tentativi, cosa che ho fatto io e tutti quelli che come me hanno iniziato diversi anni fa.</p>
<p>Oggi lo scenario è ben diverso, ci sono libri in italiano che parlano di questo argomento, sia libri scritti da autori italiani (come il mio) che libri americani tradotti in italiano. Ci sono tanti corsi, sia online che in aula e io, ad esempio, ho realizzato il <a href="http://raffaele.link/growth-hacking-masterclass">primo corso online sul Growth Hacking</a>, proprio insieme a Luca, dove trattiamo l'argomento dalla A alla Z.</p>
<p>A questo aggiungi il fatto che anche le università si sono svegliate e ne hanno capito l'importanza. Oggi ci sono sia percorsi completamente dedicati al Growth Hacking che moduli o lezioni su questo tema nei "classici" corsi dedicati al marketing o al business.</p>
<p>Quindi il materiale per studiare c'è ed è pure tanto. Ovviamente questo da solo non basta, visto che è una materia molto pratica. Come ho scritto sul mio blog io credo nel metodo "studia-sperimenta-aspetta". Quindi dopo una bella infarinatura teorica non c'è modo migliore di imparare qualcosa di nuovo se non sporcandosi le mani e smanettando su progetti veri!</p>
<p>In generale considera che la figura del Growth Hacker è quella che nel mondo delle risorse umane viene chiamata "profilo a T" e quindi è una persona con un bagaglio di competenze incredibili, proprio perché spazia da aspetti tecnici alle vendite, dal saper scrivere al saper fare advertising, dal marketing all'analytics e chi più ne ha più ne metta!</p>
<p><strong>(M) Come ultima domanda ti chiedo: mettiamoci nei panni di una startup neonata che ha zero euro, un prodotto in fase di realizzazione e bisogno di crescere: quali tool consiglieresti, i più economici possibili, per iniziare a monitorare le proprie metriche? Si può fare con zero euro?</strong></p>
<p>(R) Il Growth Hacking non si può fare con zero euro, togliamocelo dalla testa. E allo stesso modo <em>non si può fare startup con zero euro</em>. È una stupidaggine che dobbiamo smettere di ripetere. Se hai zero euro vai a fare il dipendente, non ti metti a fare la cosa più rischiosa al mondo: l'imprenditore.</p>
<p>Detto questo i soldi li devi trovare, che siano i tuoi risparmi o che siano quelli di un secondo lavoro che fai per mantenerti, un piccolo budget è necessario.</p>
<p>La grossa differenza del Growth Hacking è che ti permette di ottenere risultati tangibili e fin da subito anche con budget molto piccoli perché ti permette di usare il budget in maniera più intelligente. La chiave è tutta qui.</p>
<p>E non è mai questione di tool, ma ancora una volta, è questione di processo e di strategia.</p>
<p>Se proprio però vuoi una risposta con qualche nome, e so che la vuoi, ti dico che allora si possono fare grandi cose anche con tool di analytics gratuiti come <a href="https://analytics.google.com/">Google Analytics</a> o con tool professionali ma che offrono piani free come <a href="https://count.ly/">Countly</a> e <a href="https://amplitude.com/">Amplitude</a>.</p>
<p>Occhio che però per crescere questo non basta. Raccogliere i dati è importante, ma è solo l'inizio. Devi sapere quali dati raccogliere, come leggerli e, soprattutto, quali decisioni prendere da essi.</p>
<p>E per questo non c'è tool al mondo. Tocca mettersi a studiare ;)</p>
<p><strong>Grazie Raf per il tuo tempo. Vi lascio con il</strong> <a href="http://amzn.to/2hOoizV"><strong>link al libro su Amazon</strong></a><strong>. alla prossima!</strong></p>
Batman ha ispirato il ... Comic Sans: storia di un Font che tutti odiano
2017-12-01T00:00:00Z
https://michelenasti.com/2017/12/01/batman-ha-ispirato-il-comic-sans-storia-di-un-font-che-tutti-odiano.html
<p>La storia che sto per raccontare riguarda uno degli aspetti più <em>mitologici</em> dell'informatica intera: il <strong>Comic Sans</strong>, il carattere più odiato in assoluto, ma allo stesso tempo uno dei più usati al mondo.</p>
<h2>L'inizio: Microsoft Bob</h2>
<p><a href="https://it.wikipedia.org/wiki/Microsoft_Bob" title="Microsoft Bob">Microsoft Bob</a> meriterebbe un articolo a parte, in quanto fu uno dei primi progetti di Microsoft gestiti direttamente da Melinda Gates (la moglie di Bill) e, contemporaneamente, uno dei progetti più fallimentari.</p>
<p><img src="https://michelenasti.com/images/bobhome1p.png" alt="" /></p>
<p>L'idea dietro a Bob era di creare un'interfaccia alternativa a Windows 3.1 e Windows 95, un'interfaccia che fosse giocosa, facile da usare per gli utenti che non avevano dimestichezza coi computer, come gli anziani.</p>
<p>Si navigava all'interno di questo salone di casa, e per leggere un libro si cliccava sul libro, così come per aprire il calendario, etc.</p>
<p>In fin dei conti l'idea era carina, ma aveva un grande problema: dietro un'interfaccia così giocosa il font utilizzato è un vetusto Times New Roman. brutto eh?</p>
<p><em>(sono segretamente convinto che sia stato questo il vero motivo dell'insuccesso commerciale.)</em></p>
<h2>Vincent Connaire: l'uomo che progettò Comic Sans</h2>
<p><a href="https://en.wikipedia.org/wiki/Vincent_Connare" title="Vincent Connaire">Vincent Connaire</a> è un designer che nel 1993 iniziò a lavorare per Microsoft.</p>
<p>Connaire entrò subito in contatto col team di Microsoft Bob e fece notare che un'interfaccia così giocosa non poteva avere un font tanto serio, quindi propose di ideare un font più uniforme allo stile comunicativo di Bob.</p>
<p>L'idea fu inizialmente accettata e Connaire si ispirò ai fumetti che amava di più, tra cui un <a href="https://en.wikipedia.org/wiki/The_Dark_Knight_Returns" title="Batman - The dark knight returns">Batman - The dark knight returns</a> del 1986 che aveva sulla sua scrivania. I caratteri dei fumetti generalmente vengono disegnati a mano, e fanno parte del processo di creazione. Il processo artigianale che c'è dietro a ogni dialogo di un fumetto fa sì che nessuno, fino ad oggi, si sia mai lamentato dei font dei fumetti.</p>
<p>Qui sotto riporto una tavola di Batman, per farvi capire da dove nasce l'ispirazione.<br />
<img src="https://michelenasti.com/images/batman-begins-1986.jpg" alt="" /></p>
<p>(Ti ricordo che all'epoca Internet non c'era, o meglio esisteva da 1 anno: molto difficile trovare ispirazione on line. Io ricordo che dovetti installare non solo Netscape ma anche i driver TCP/IP a mano, coi floppy, su Windows 3.1).</p>
<h2>Il Comic Sans</h2>
<p>Fu così che nacque il Comic Sans, un carattere fortemente ispirato ai fumetti, che però non devi MAI utilizzare se vuoi disegnare fumetti per professione, pena il licenziamento.</p>
<p><img src="https://michelenasti.com/images/ComicSans.gif" alt="" /></p>
<p>Tuttavia, quando il font era ormai pronto, <strong>il team di Microsoft Bob lo scartò</strong>: non perchè fosse brutto, bensì perchè il progetto era ormai completato e <strong>tutte le textbox erano state ottimizzate per il Times New Roman</strong>. Modificare il font significava entrare in ogni possibile textbox e aggiustarle per rendere il testo visualizzabile correttamente, cosa non proprio praticabile (all'epoca non esistevano reponsive design -- la risoluzione "normale" era 800x600, credo...).</p>
<p>Quindi, se il Comic Sans non venne al mondo tramite Microsoft Bob, come si è diffuso?</p>
<h2>Come è diventato il font più conosciuto al mondo: 3D Movie Maker</h2>
<p>In un'epoca in cui Microsoft aveva il monopolio totale di ogni cosa fosse installata sui vostri pc, i programmatori di un altro progetto Microsoft decisero di usare il Comic Sans per le vignette di aiuto di un software chiamato <strong>3D Movie Maker</strong>, un applicativo per bambini che permetteva di inserire personaggi in un ambiente virtuale (nel 1995, eh!).</p>
<p><img src="https://michelenasti.com/images/3dmoviemaker.jpg" alt="" /></p>
<p>Su youtube esistono dei filmati che mostrano <a href="https://www.youtube.com/watch?v=LNBCoaZn8FU">3D Movie Maker in azione</a>.</p>
<p>Da quel momento in poi, l'ascesa di Comic Sans fu inarrestabile: fu inserito in <strong>Windows 95</strong>, fu uno dei caratteri di default di <strong>Microsoft Publisher</strong> e del neonato <strong>Internet Explorer 3.0</strong>.</p>
<p>Il Comic Sans è stato usato in molte situazioni sconvenienti. Ad esempio, il font stato usato per il testo di un memoriale di guerra, ai lati delle ambulanze, nei CV. In rete esistono centinaia di pagine sull'uso sconveniente del font, ad esempio su <a href="https://www.buzzfeed.com/sophiegadd/absolutely-beautiful-examples-of-comic-sans-in-the-wild?utm_term=.brjVv42rMK#.pmnvYMPbkN">BuzzFeed</a>.</p>
<p>Io personalmente posso dire di aver sostenuto <strong>esami all'università</strong> la cui traccia era scritta in Comic Sans.</p>
<h2>E ora?</h2>
<p>Beh, dovrebbe essere chiaro, <strong>non devi usarlo</strong>. Per. nessun. motivo. al. mondo.</p>
<p>Ci sono pochissimi motivi per cui dovresti usare il comic sans, e te li elenco qui:</p>
<ul>
<li>Se la tua audience ha meno di 2 anni.</li>
<li><strong>Se la tua audience è composta da dislessici</strong>. E' infatti noto che i dislessici trovano più facile leggere il comic sans rispetto agli altri font.</li>
</ul>
<hr />
<p>Questo articolo non sarebbe mai nato senza il primo capitolo di un libro che purtroppo ho perso in aereo: <a href="http://amzn.to/2zDrYID" title="Sei proprio il mio Typo ">Sei proprio il mio Typo, di Simon Garfield</a>.</p>
<p><a href="http://amzn.to/2zDrYID"><img src="https://michelenasti.com/images/seproprioilmiotypo.jpg" alt="" /></a></p>
<p>Per altre storie bizzarre sui font, questo è il libro che fa per te;)</p>
Alla scoperta di VueJS
2017-12-03T00:00:00Z
https://michelenasti.com/2017/12/03/alla-scoperta-di-vue-js.html
<p>E' domenica, il Napoli non gioca, che fare? Dopo aver acceso il camino ho iniziato a guardarmi un po' di VueJS.</p>
<p><img src="https://michelenasti.com/images/vuejs.png" alt="" /></p>
<h2>VueJS, Un framework minore?</h2>
<p>In questo mondo dominato da Angular e React, c'è ancora spazio per altri framework frontend?</p>
<p>Ti faccio un'altra domanda: quando ai suoi tempi c'era <em>solo</em> JQuery, riuscivi a pensare che sarebbe mai nata un'altra libreria per il frontend?</p>
<p>Penso di aver sentito parlare la prima volta di Vue che c'era <em>solo</em> Angular 1 in giro, e tutti gli altri framework sgomitavano per diventare famosi. C'è chi ce l'ha fatta, tipo React, e chi invece ci prova ancora e non ci riesce, tipo Ember.</p>
<p><strong>La differenza, molto spesso, la fa l'azienda che sponsorizza</strong>: Google per Angular, e Facebook per React.</p>
<p>Nel caso di Vue, le aziende sponsorizzatrici non sono proprio dei nomi di primo piano: <a href="https://stdlib.com/" title="StdLib">StLib</a>, un'azienda che realizza un framework per codice serverless, <strong>non la conoscevo prima di oggi</strong>. Quindi, già su questo, partiamo svantaggiati.</p>
<h2>Cosa mi ha colpito di VueJS</h2>
<p>L'approccio di VueJS è molto semplice, e questo è uno dei principali punti di forza:</p>
<ul>
<li><strong>non c'è bisogno di partire da un boilerplate</strong>, ma basta un file html linkato a uno script js;</li>
<li><strong>non c'è bisogno di conoscere la programmazione super avanzata</strong>, ma bastano le conoscenze di un normale programmatore;</li>
<li><strong>Somiglia in tutto e per tutto al primo angular</strong>, quello che ho imparato a conoscere dannatamente e bene, e che funzionava alla grande;</li>
<li><strong>la documentazione è chiarissima</strong> e sin dall'inizio si entra nel vivo di VueJS.</li>
</ul>
<p>Non mi sono ancora addentrato molto, ma ho notato che il sistema dei componenti è semplicissimo e soprattutto <strong>si può iniziare a lavorare senza mettere in mezzo i vari webpack</strong>, systemjs, browserify e compagnia cantando.</p>
<p>A questo punto una domanda me la sono fatta anch'io:</p>
<h2>VueJS è un framework moderno?</h2>
<p>Nel senso, ha quelle caratteristiche di un <strong>framework post-angular1</strong> (utilizzo di un virtual dom, utilizzo di paradigmi reactive, server-side rendering...) ?</p>
<p>Il team di Vue ha scritto un'analisi tecnica molto profonda di Vue vs ogni-altra libreria-frontend sul mercato, soffermandosi specialmente su React perchè è quella cui somiglia di più. La velocità non deve essere presa in considerazione come fattore determinante, perchè Vue e React hanno numeri molto simili; quello che cambia è in dettagli come la curva di apprendimento, cosa accade all'intera applicazione un componente si aggiorna, etc.</p>
<p>Questo vuol dire che si, <strong>Vue è una libreria frontend che si occupa solo della view</strong>, lasciando ad altre librerie il compito del routing o di altre cose belle (autenticazione, autorizzazione, chiamate http...).</p>
<h2>Sono solo all'inizio</h2>
<p>A parte il primo tutorial, che mi ha lasciato un'ottima impressione, c'è ancora tanto da guardare. Vorrei provare a sviluppare qualcosa di meno semplice così da farmi un'idea di come funziona quando le linee di codice aumentano. Resisterà VueJS alla prova della complessità?</p>
<p>Nei prossimi articoli proverò ad entrare più nel dettaglio di come funziona Vue. Keep reading!</p>
Dovrei partecipare al programma Erasmus?
2017-12-04T00:00:00Z
https://michelenasti.com/2017/12/04/dovrei-partecipare-al-programma-erasmus.html
<p><strong>Anche io ho partecpato al programma Erasmus</strong>, era il 2006, l'anno dei mondiali e io ero a Siviglia. <em>Non avete idea di quanto sia appagante vincere un mondiale all'estero</em>.</p>
<p>Basandomi sui miei ricordi: prima di partire <strong>hai paura</strong>, il primo mese che stai lì ti chiedi <strong>ma chi te l'ha fatto fare</strong> mentre cerchi casa, fai la spesa, ti iscrivi ai corsi; il secondo mese è insieme al primo il più duro, <strong>pensi di aver fatto la cazzata più grande della tua vita</strong>, non capisci la gente quando parla, non sai cosa fare e dove andare, un po' ti senti solo se non hai nessuno (e io partii da solo)...</p>
<p><strong>Poi venne fuori la faccia di bronzo</strong>. Un giorno sentii parlare in italiano in autobus e chiesi "hey, sei Erasmus? ma dove diavolo uscite la sera?"; un altro giorno incontrai degli studenti spagnoli che festeggiavano un'Italian Day con dolci italiani (disgustosi e mai mangiati prima) e in pratica finii a uscirci insieme e a correggergli i compiti, etc etc.</p>
<p><strong>Il terzo mese inizi a pensare che l'Erasmus è una figata pazzesca</strong>, il quarto il quinto e il sesto <strong>speri non finisca mai</strong> e inizi a progettare fughe, rimpatriate, anche se già sai che tante cose dovranno necessariamente finire lì.</p>
<p>E in tutto questo c'è che ti lasci con la ragazza o ne trovi una nuova, si conoscono persone di tutto il mondo, si imparano nuove lingue e si girano continenti. <strong>Anche i sentimenti si mettono a dura prova.</strong></p>
<p>Dal punto di vista accademico ho avuto un problemaccio con lo spagnolo andaluso, In pratica i primi tre mesi di corso non ho capito nulla. Io, mai bocciato, in Spagna feci un solo esame su tre preventivati, ma quando tornai in Italia superai quei due esami con 30&lode.</p>
<p><strong>Chiunque dovrebbe fare l'Erasmus. Anche chi non va all'università. Specialmente chi è fidanzato/a. Specialmente chi ha paura.</strong></p>
<p><strong>Tutti.</strong></p>
<hr />
<p><em>Quest'articolo era, in principio, un commento a un post su facebook. In bocca al lupo a tutti quelli che parteciperanno al programma.</em></p>
VueJS basics
2017-12-17T01:00:00Z
https://michelenasti.com/2017/12/17/vuejs-basics.html
<p>It has been a long week for me, I'm studying very hard VueJS - this time by coding real examples - and I confirm that there are some very good concepts backed in.</p>
<p><img src="https://michelenasti.com/images/vuejs.png" alt="" /></p>
<h2>Separation of data and methods</h2>
<p>This is a basic html containing some VueJS markup:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span><span class="token punctuation">></span></span><br /> ...<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.jsdelivr.net/npm/vue<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>{{ message }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>{{ sayHelloTo(name) }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>This is a basic VueJS element:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">el</span><span class="token operator">:</span> <span class="token string">'#app'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">data</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">message</span><span class="token operator">:</span> <span class="token string">'Hello from Vue!'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Michele'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">sayHelloTo</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">!</span><span class="token template-punctuation string">`</span></span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Let's describe this snippet:</p>
<ul>
<li>the <code>el</code> references the DOM element with the <code>#app</code> id. This element will be <em>Vuezed</em>.</li>
<li>the <code>data</code> object contains the data that can be used inside the HTML: for example, we used the <code>message</code> property in the Html.</li>
<li>the <code>methods</code> property contains the methods you can use in your html. These are useful for calculations or for actions, like clicking on a button.</li>
</ul>
<p>I acutally like a lot: a clear separations of data and methods.</p>
<p>This is a concept that comes from the <strong>functional programming world</strong>, that is, separate the data from the functions that interact with this data.</p>
<p>However, Vue is not <em>functional</em> in the classic sense of the definition, because you use the <code>this</code> keyword to access data & you're not forced to write pure functions, etc etc.</p>
<h2>A word on this</h2>
<p>Have you noticed that from the <code>methods</code> section, if you want to access <code>data</code> properties, you use <code>this</code> ?</p>
<p>In fact, Vue proxies the <code>this</code> keyword so that you can access to data without writing <code>this.data.message</code>.</p>
<p>However, for <code>this</code> to work you must write 90% of the time the old ES5 <code>function()</code> declaration: this is expecially clear in VueJS components, an argouments we'll talk in another post.</p>
Essere programmatori e vivere di progetti propri
2018-01-06T00:00:00Z
https://michelenasti.com/2018/01/06/essere-programmatori-e-vivere-di-progetti-propri.html
<p>Inauguriamo una nuova sezione, ossia "Michele risponde" :)</p>
<p>Mi capita spesso di rispondere a domande su gruppi facebook o nei forum, tanto vale appuntarsele anche qua.</p>
<p>Mi prendo la libertà di modificare un po' le domande se sono lunghe.</p>
<p><strong>Domanda:</strong></p>
<blockquote>
<p>[...] Salve ragazzi, forse oggi andrò un po’ OT, perché vorrei chiedervi un consiglio più a livello personale che tecnico.<br />
Sono un ragazzo di 17 anni e da un anno a questa parte mi sono buttato sull’informatica perché lo trovo un settore abbastanza meritocratico, perciò in questo anno ho imparato i linguaggi per creare grafiche di pagine web (html;css) e ho iniziato un progetto che fino ad ora va bene.<br />
In questi mesi ho pensato molto a come muovermi ed ampliare le mie conoscenze, e dopo vari ripensamenti, per questo negli ultimi tempi ho deciso di imparare C (un linguaggio che opera strettamente “vicino” all’hardware, per imparare le fondamentali logiche e operazioni dei computer)<br />
Premetto che io non voglio aumentare le mie skills per poi assicurarmi un posto di lavoro, perché non vorrei diventare un dipendente, ma sviluppare in proprio i miei progetti e magari, perché no, un giorno viverne di essi .<br />
Ora arriviamo al punto, sto imparando C, forte solo di una ottima/discreta base di matematica, sono arrivato a trattare argomenti quali puntatori e strutture dati, però spesso penso che ha una applicazione, non so da dove partire, cioè per intenderci, io imparo le nozioni per definirmi anche un buon/discreto conoscitore di, ma una volta apprese quelle, non so da dove partire per sviluppare qualche mio programma in proprio, pecco di ispirazione e mi scoraggio e tra me e me penso "per quale motivo stai imparando il C se non hai nessun fine/scopo o anche idea di applicazione?"<br />
Io ora chiedo se a qualcuno di voi sia già capitata una cosa del genere, come vi siete mossi, quale settore dell’informatica può essere un potenziale, un fondamento da cui poter costruire qualcosa, insomma una fonta di ispirazione per iniziare a mettersi in proprio.</p>
</blockquote>
<p><strong>Risposta:</strong></p>
<p>Uhm. Ci vedo due cose distinte. Imparando il C diventi un ottimo tecnico: si può vivere già di quello, magari lavorando per altri. Questo da solo è un ottimo piano B da mettere in pratica finchè non hai idee che ti prendono al100%.</p>
<p>Tu però hai altre ambizioni, ossia realizzare i tuoi progetti e vivere di quello. E ti mancano i progetti. Come tirarli fuori?<br />
La cosa più semplice che mi viene da consigliarti é di creare software che risolve i tuoi problemi. <em>Esempio di problema</em>: quando tira il vento forte nel paese in cui vivo, questo mi distrugge il giardino. S_oluzione_: ho scritto un programma che mi avvisa quando sta per arrivare quel tipo di vento.</p>
<p>Io non ho inventato nulla, le App per il meteo esistono da sempre; ma avevo un'esigenza particolare e nessuna app all'epoca me la risolveva, così ho inventato la mia soluzione. Magari serve anche ad altri? Girando un pò per la rete devi essere bravo e fortunato a trovare qualcuno disposto a pagare per i tuoi software.</p>
<p>E qui viene la parte <strong>difficile</strong>: farsi pagare é una delle cose più difficili in assoluto. Che sia 1€ o 10000€, il furbo che "ci prova" lo trovi sempre. Alcuni dicono che mettersi in proprio é difficile proprio perché bisogna continuamente trovare clienti, lavorare per loro, e <strong>perdere una quantità di tempo immane per farsi pagare</strong>...</p>
<p>![](/images/Best Money Making Computer Fields.jpg "people won't throw money at you")</p>
<p>E per finire: l'informatica (a livello di tecnici) non é meritocratica. Va avanti non il più bravo (tranne forse in Mozilla e in Google) ma chi da meno problemi al boss, ossia quello di cui ci si può fidare. Questo l'ho appreso sulla mia pelle dopo qualche anno di esperienza, e ovviamente vale in contesti aziendali. Nel momento in cui avrai tu un'azienda coi tuoi fantastici software, chi promuoverai come "capo team"? il più bravo a organizzare il gruppo, o il più bravo tecnico che hai? Scoprirai che le due figure di solito non coincidono.</p>
<p>Un ultimo consiglio, che do sempre a chi vuole organizzare qualcosa in proprio: partecipa a uno <a href="https://www.google.it/search?q=startup+weekend+italia&ie=utf-8&oe=utf-8&client=firefox-b-ab&gfe_rd=cr&dcr=0&ei=1FBRWoqRCqHBXvOUqYgG">Startup Weekend</a>. Ne organizziamo uno anche a Salerno, anche se ancora non abbiamo una data certa. Lì troverai tutto ciò che cerchi: persone che vogliono fondare la loro azienda, idee innovative, e come realizzarle dal punto di vista pratico (validazione, business plan, analisi dei competitor, stima dei tempi, etc.)</p>
<p>In bocca al lupo!</p>
E' importante conoscere come sono fatte le librerie che usiamo?
2018-01-07T00:00:00Z
https://michelenasti.com/2018/01/07/e-importante-conoscere-come-sono-fatte-le-librerie-che-usiamo.html
<p>Domanda:</p>
<blockquote>
<p>fin dal primo giorno, alla facoltà di Ingegneria Informatica mi è stata inculcata un'idea che tutt'ora fatico ad accettare. Visto che ci sono infinite librerie che ti rendono disponibili delle funzioni avanzate, il tuo lavoro principale non è riscrivere quelle funzioni, ma saperle usare e adattare per i tuoi scopi. Faccio un esempio stupido: una libreria python per l'elaborazione di file pdf. Due righe e posso copiare, fondere, togliere pagine. <strong>Però non saprò mai come funziona.</strong> <strong>E questa cosa mi angoscia tantissimo.</strong> Nel senso, posso aprire la libreria e andarmele a leggere, ovviamente. Ma passerei probabilmente ore a capire il loro funzionamento. Sono l'unico che è turbato da questa cosa? Chiaramente mi riferisco a chi sta ancora studiando, come me.</p>
</blockquote>
<p>Risposta:</p>
<p>Nel thread originale, su facebook, ho risposto che <strong>da studente non c'è niente di meglio che provare a rifare quelle librerie proprio per capire come sono state fatte.</strong> All'università sono felice di aver reimplementato tutte le strutture dati, perchè ora le uso con cognizione di causa.</p>
<p>Un altro esempio: quando ho iniziato a studiare NodeJS, sentivo la mancanza di una libreria di logging come Log4J in Java. Una delle prime cose che ho fatto è stata di scrivermene una mia, che ovviamente faceva schifo ed era pure piena di bug, ma che mi ha aiutato a imparare meglio proprio NodeJS.</p>
<p><img src="https://michelenasti.com/images/rinventare-la-ruota.jpg" alt="" /><strong>Nel mondo del lavoro vero c'è una discriminante: il tempo.</strong> Gli studenti scopriranno prima o poi che ogni giorno di lavoro in più equivale a molti soldi spesi dall'azienda, quindi meno tempo si impiega meglio è. E questo impone che <strong>non si può reinventare la ruota,</strong> non si può riscrivere ogni possibile funzione che abbiamo già disponibile da qualche parte on line. <strong>E' anche il motivo per cui delle persone si sono inventate i framework</strong>: <strong>risolvono problemi che altri già hanno avuto</strong>, risparmiando tutto il tempo necessario a tirare su un progetto.</p>
<h3>L'eccezione che conferma la regola: cosa ci insegna Elon Musk</h3>
<p><img src="https://michelenasti.com/images/Elon_Musk_2015.jpg" alt="" /></p>
<p>Ho tirato in ballo un <em>pezzo da 90</em> e l'ho fatto per un motivo ben preciso. Tesla è un'azienda che produce auto elettriche. Era da una settantina d'anni che non si fondavano nuove case automobilistiche <em>consumer,</em> e infatti ciò che ha realizzato, rivisto con gli occhi di oggi, sembra comunque una pazzia. Per creare un'auto dal nulla non ha seguito il solito processo che impiegano gli altri costruttori: <strong>ogni singolo pezzo delle Tesla viene prodotto da loro stessi.</strong></p>
<p>E ancora: quando ha fondato SpaceX si stava buttando in un settore dominato da poche grandi aziende che avevano formato un oligopolio. Era difficile e dispendioso trovare, tra le tante cose, una base di lancio per poter sperimentare i propri razzi costruiti <em>in-house</em>, <strong>senza subappaltare nulla</strong>. Quindi che fa Musk? <strong>Compra un atollo nel Pacifico da cui farà partire tutti i suoi razzi.</strong> In questo modo non ha dovuto più chiedere nulla a nessuno, e ha velocizzato di molto il processo di realizzazione e sperimentazione.</p>
<p><em>Musk ha provato a "usare quanto c'era di già fatto"</em>, sia nel caso di Tesla che di SpaceX, ma a un certo punto si è trovato nella condizione di dipendere troppo da altro e di dover riciclare soluzioni non ottime per il suo contesto. Per Tesla, i motori erano sviluppati per altri tipi di carburanti, le batterie erano pensate per altri sistemi etc. <strong>Riprogettare tutto da zero era l'unica strada percorribile per ottenere il miglior progetto, e soprattutto la miglior execution possibile.</strong></p>
<p>La lezione che possiamo trarne è che <strong>se dipendiamo in maniera decisa da qualcosa, e questa non ci soddisfa, è il caso che ce la riscriviamo da noi</strong>.</p>
<p>Se il <em>core</em> della tua azienda è, ad esempio, processare pagamenti con le carte di credito, e le attuali soluzioni presenti sul mercato costano troppo o sono lente, è il caso che tu ti scriva il tuo payment processor.</p>
<p>Sarà lungo e non sarà una passeggiata, ma dopo avrai la miglior soluzione possibile.</p>
<p>Poi la devi vendere, ma questa è un'altra storia :)</p>
I trend Javascript del 2018
2018-01-09T00:00:00Z
https://michelenasti.com/2018/01/09/i-trend-javascript-del-2018.html
<p><strong>Quali sono le "cose" da tenere maggiormente d'occhio nello sviluppo JavaScript nel 2018?</strong></p>
<p>Ci sono alcuni trend che si sono sviluppati verso la fine del 2017 e che dovranno necessariamente essere inseguiti.</p>
<p><img src="https://michelenasti.com/images/trends.jpg" alt="" /></p>
<ul>
<li><strong>Async/await</strong>: praticamente l’aggiunta più utile al linguaggio da qualche anno a questa parte; ora è possibile programmare in asincrono con estrema facilità. Da studiare obbligatoriamente</li>
<li><strong>Webpack</strong>, <strong>rollup, parcel, etc.</strong>: i bundler stanno diventando il nuovo terreno di sfida, specialmente nel frontend. Capire come funzionano, come estenderli e come usarli per le proprie esigenze darà sicuramente un vantaggio competitivo.</li>
<li><strong>Typescript</strong>: i tipi statici sono una manna dal cielo, permettono di fare refactoring molto più rapidamente. Impararlo è molto semplice e la sfida sarà usarlo su NodeJS e sugli altri framework (react, vue…)</li>
<li><strong>Testing</strong> <strong>(frontend, backend)</strong>: c’è troppo poco testing js in giro. Manca il test del frontend, manca il test del backend. Inoltre esistono decine di framework di testing differenti che si danno battaglia. Di sicuro, per un linguaggio dinamico, il testing diventa essenziale se vuoi costruire applicazioni grosse.</li>
<li><strong>Modularizzazione (Frontend) /Microservizi</strong> <strong>(backend)</strong>: questo vale per tutti i linguaggi: il trend del momento è realizzare microservizi che lavorano insieme. Capire come strutturare applicazioni di questo tipo diventa fondamentale per uno stack moderno.</li>
<li><strong>VueJS (frontend)</strong>: in un mondo dominato da Angular e React, Vue si propone come una delle tante alternative, di sicuro quella che ha attirato più consensi. Io personalmente lo conosco e lo uso con piacere.</li>
<li><strong>GraphQL</strong>: è un’alternativa a REST che permette di semplificare il modo di richiedere i dati a un server. I big l’hanno già scelto, per l’enterprise invece c’è ancora tanto da fare.</li>
</ul>
<p>Buono studio!</p>
La mia esperienza col Test Driven Development
2018-01-12T00:00:00Z
https://michelenasti.com/2018/01/12/la-mia-esperienza-col-test-driven-development.html
<p>Il <strong>Test Driven Development</strong> è una pratica <em>mistica</em>: consiste nello <strong>scrivere prima i test e poi il vero e proprio codice che ti serve per realizzare quella funzionalità</strong>.</p>
<p><img src="https://michelenasti.com/images/Testing_in_Progress.gif" alt="" /></p>
<p>Faccio un esempio: immaginiamo di dover realizzare una funzione <code>cercaSuCanale5()</code> che, presa in input una data (es. <code>2018-12-31</code> e una stringa (es. <code>concertone</code>) ritorni tutti i programmi che su Canale quel giorno 5 contengono quella stringa nel titolo o nella descrizione.</p>
<p>E' un caso semplice, magari richiederà qualche ricerca per trovare le API da interrogare per tirare fuori i dati giusti, oppure se abbiamo questi dati disponibili nel db li andremo a prendere da lì, ma <strong>è un caso abbastanza interessante per discutere di testing</strong>.</p>
<p>Se realizzo questa funzionalità col TDD, prima ancora di scrivere la vera e propria funzione <code>cercaSuCanale5()</code> , scriverò questi casi:</p>
<ul>
<li>un test che deve rispondere con un'eccezione quando chiamo la funzione con una data <code>null</code>.</li>
<li>Un test che deve rispondere con <em>tutta la programmazione televisiva di quel giorno</em>, se invio una data, ma la stringa è null o vuota.</li>
<li>Un test che, data una data valida e una stringa (es. <code>concer</code>) ritorna gli eventi che contengono questa stringa nel titolo o nella descrizione per quella data.</li>
<li>Un test che, data una data valida e una stringa implausibile (es. <code>abcdefgh123</code>) ritorna una lista vuota.</li>
</ul>
<p>Hai notato? <strong>Solo per scrivere questi test abbiamo in pratica definito come si comporta la nostra funzione.</strong> Questo è un ottimo motivo per fare TDD: ha un funzione <em>documentativa</em> del codice.</p>
<p>Tornando ai test, la cose si fanno difficili quando la nostra funzione accede a qualcosa di esterno per elaborare i dati. Ad esempio, se <code>cercaSuCanale5()</code> chiama un'API esterna, chi ci garantisce che per la stessa data e la stessa stringa i dati saranno sempre gli stessi? Stesso dubbio per un'eventuale implementazione su DB: magari chi gestisce API o DB impone che dopo 1 anno (o un giorno!) quei dati vengano cancellati, per cui il test dopo 1 anno (o un giorno!) non funzionerà più.</p>
<p>Per evitare tutto questo, in genere nel test si <em>mocka</em> la chiamata a tutto ciò che è esterno, ossia si dice al test che quella chiamata esterna deve essere sostituita da un <strong>mock</strong> che risponderà sempre allo stesso modo.</p>
<p><em>Già qui i detrattori del TDD diranno che, nel momento in cui introduciamo i mock, stiamo sostituendo il mondo reale con una finzione e tanti casi reali non saranno più riscontrabili. Purtroppo è vero, un mock è una semplificazione, ma è l'unica opzione che abbiamo quando abbiamo a che fare con dati mutabili.</em></p>
<p>Ecco altre fantastiche "regole" del TDD:</p>
<ul>
<li><strong>i test scritti devono fallire tutti prima di scrivere la vera e propria funzione</strong></li>
<li><strong>mai scrivere codice di produzione senza un test che fallisce</strong></li>
<li><strong>se accade un bug in produzione, scrivi prima un test che lo riproduce e poi correggi il bug (e il test)</strong></li>
</ul>
<h2>La mia esperienza col TDD</h2>
<p>Alla mia prima esperienza lavorativa mi hanno iniziato al TDD: "<strong>scrivi prima il test e poi il codice</strong>. In questo modo avrai chiaro cosa il tuo codice deve fare, e come".</p>
<p>Addirittura una delle pratiche era che <strong>quando scaricavamo un modulo da SVN dovevamo lanciare i test, se questi fallivano potevamo anche <em>richiamare</em> l'ultimo collega che aveva committato senza correggere i test.</strong></p>
<p>Bello.</p>
<p><strong>Poi abbiamo iniziato a correre</strong>, perché le scadenze non venivano rispettate, e la prima cosa che succede quando corri è che butti a mare la qualità. Di conseguenza i test iniziarono a scarseggiare.</p>
<p>Inoltre, questo lo notano pochi, <strong>i test sono altro codice da manutenere</strong>: quando fai una modifica al codice dell'app (e il codice cambia, perchè cambiano i requisiti) devi inevitabilmente fare modifiche anche al codice del test, e questo porta via fino al doppio del tempo. Quante aziende hanno schedule così <em>rilassate</em> da poterselo permettere?</p>
<p>Alla fine faccio alcuni test unitari, molti test di integrazione in cui mocko db o chiamate esterne, e la priorità é di testare ciò che é difficile da testare a mano.</p>
<p>Tutto questo discorso vale, nel mio caso, per il backend, perché <strong>per il frontend mi sono ritrovato da solo a voler scrivere test - per gli altri non era nemmeno un'opzione!</strong></p>
<p>Non mi arrendo però: ora che devo mettere mano a codice React e Vue, voglio assolutamente riprovare a scrivere codice di test per il frontend, e vedere a quali benefici porta.</p>
Perchè la Green Card è una lotteria?
2018-01-17T00:00:00Z
https://michelenasti.com/2018/01/17/perche-la-green-card-e-una-lotteria.html
<p>Ho beccato un post on line in cui si chiedeva se un lavoratore da remoto può lavorare dagli USA per clienti italiani. La risposta precisa ve la potrà dare solo un avvocato esperto (o il consolato USA); semplifichiamo dicendo che se si lavora dal territorio USA bisogna pagare le tasse americane. Per lavorare legalmente in USA ci sono <a href="https://www.us-immigration.com/greencard/Green-Card-Lottery.html">molte strade</a>: la <strong>Green Card</strong> è una di queste.</p>
<p>In questo articolo parleremo del <strong>perchè gli USA hanno scelto la lotteria come sistema di assegnazione</strong> (e perchè secondo me hanno fatto bene).</p>
<p><img src="https://michelenasti.com/images/green_card_lottery.jpg" alt="" /></p>
<h2>Cos'è la Green Card?</h2>
<p>La <a href="http://america24.com/news/come-si-ottiene-la-green-card"><strong>Green Card</strong></a> è l'equivalente del <em>permesso di soggiorno italiano</em>, ma permette di lavorare, a tempo indeterminato, negli USA. In pratica chi vince una Green Card può tranquillamente trasferirsi e creare reddito: non ci sono limiti di nessun tipo (ma evitate di violare la legge).</p>
<h2>Perchè si dice che è una lotteria?</h2>
<p>Perché lo è: il numero di posti messi a disposizione ogni anno è di circa 50.000, a fronte di un numero enorme di richiedenti. E' una lotteria per promuovere la diversità, quindi si cerca di far arrivare negli States immigranti da qualsiasi area del mondo (tranne alcune nazioni che hanno in essere embarghi con gli Stati Uniti).</p>
<p><img src="https://michelenasti.com/images/lottery.jpg" alt="" /></p>
<p>Per molti tutto ciò potrebbe sembrare un'<strong>americanata,</strong> nel senso che magari c'erano altri modi per assegnare questa risorsa a quanti ne richiedevano. Il problema è che tutti gli altri sistemi hanno pro e contro, e la lotteria è semplicemente il metodo più equo, rapido, ed economico.</p>
<p>Sostanzialmente quando vuoi offrire una risorsa a qualcuno hai molti modi per farlo:</p>
<ul>
<li>L'<strong>assegnazione diretta</strong> è il metodo più clientelare che c'è: io titolare di una risorsa (es. un appalto) decido il vincitore in maniera del tutto arbitraria.</li>
<li>Il <strong>bando di gara "beauty contest"</strong> prevede che tu la commissione scelga il progetto migliore. Anche in questo caso la corruzione la fa da padrona.</li>
<li>Le <strong>aste al ribasso o al rialzo</strong> sono frequenti nel settore dei diritti televisivi o per gli appalti: in questo caso, aziende con forti disponibilità vinceranno sicuramente, o comunque se sai chi altro partecipa e qual è la loro disponibilità economica (ad esempio corrompendo le persone giuste), ti basterà fare un'offerta leggermente maggiore. In questo caso chi ci perde è chi assegna il bene: non intasca il reale valore di quel bene, ma il valore appena necessario per ottenerlo.</li>
<li>L'<strong>asta di secondo prezzo</strong> è un meccanismo intelligente, anche eBay funziona così, e su questa cosa <a href="https://it.wikipedia.org/wiki/William_Vickrey">Vickrey ci ha vinto un premio nobel per l'economia</a>. Lo spiego in parole povere: facciamo un'asta a busta chiusa, ognuno di noi offre quanto secondo lui quel bene "vale", e vince ovviamente chi offre di più MA paga il secondo prezzo più alto. Tramite una serie di teoremi (che a suo tempo ho anche studiato) si può dimostrare che chi partecipa a questo tipo di asta è meno invogliato a offrire di meno rispetto al valore reale.</li>
<li>il <strong>Click Day,</strong> mi vergogno addirittura a metterlo in questa lista, è una di quelle cavolate all'italiana venute fuori dalla mente di chissà quale burocrate. Praticamente in un certo giorno e a una certa ora, si fa una gara di velocità: i più veloci a <em>cliccare</em> si assegnano quel bene_._ Capirete che tutto dipende dall'affidabilità del sistema di rilevamento, dalla latenza della velocità internet, dalla dimestichezza che ha l'operatore con mouse e tastiera, e dalla fortuna.</li>
<li>E veniamo infine alla <strong>lotteria</strong>: è il meccanismo più equo ed economico quando hai un tot di posti disponibili (es. 50.000) e una quantità enorme di persone che partecipa (es. 10.000.000): semplicemente decide il caso. Costa poco, è di facile realizzazione, non richiede di revisionare tutte le domande per verificare titoli o altro (beauty contest), nè è accessibile solo ai ricchi (aste). Ah, e non vince il più veloce.</li>
</ul>
<p>Se avete seguito uno dei primi link di questo articolo sappiate che vincere la lotteria per la Green Card è solo il primo passo: seguiranno un paio di anni di colloqui con l'ambasciata, e tanta burocrazia. <strong>La cosa più semplice é di trovare un'azienda USA che sponsorizzi la vostra candidatura per farvi lavorare lì</strong>. In bocca al lupo!</p>
Remap backtick, ñ and tilde characters on Windows computers
2018-01-27T00:00:00Z
https://michelenasti.com/2018/01/27/remap-backtick-tilde-on-windows.html
<p>Since the switch to Windows I had a problem to solve as soon as possible: find a way to write the backtick (`) and tilde (~) characters.</p>
<p>Both the characters, on a mac, can be stroked by pressing <code>AltGr + \</code> (backtick) and <code>AltGr + 6</code> (tilde).</p>
<p>On Windows the actual combination is incredible and unmemorizable - and, on my Dell XPS laptop, I just can'tuse it. It requires me to use the numeric pad (that I don't have) so I have to press the function key... well, it's a lot of strokes only to gain a single character.</p>
<p>So, that's my solution.</p>
<h2>AutoHotkey</h2>
<p>The first step is to <a href="https://autohotkey.com/" title="AutoHotkey official website">install AutoHotkey</a> from the official website.</p>
<p><img src="https://michelenasti.com/images/autohotkey.jpg" alt="" /></p>
<p>What is AutoHotkey? Here is their official description:</p>
<blockquote>
<p>AutoHotkey is a free, open-source scripting language for Windows that allows users to easily create small to complex scripts for all kinds of tasks such as: form fillers, auto-clicking, macros, etc.</p>
</blockquote>
<p>What we really need is the ability to remap some keys for our purposes.</p>
<h2>Write a AutoHotkey script</h2>
<p>Once downloaded, we need to create the AutoHotkey script with our desired configuration.</p>
<p>AutoHotkey doesn't do anything on its own; it needs a script to tell it what to do.</p>
<p>So we have to create a file <code>startup.ahk</code> to fill in with our commands.</p>
<p>Here is mine:</p>
<p><^>!6::
Send ~
Return</p>
<p><^>!::
SendRaw ```
Return</p>
<p><^>!n::
Send ñ
Return</p>
<p>Explanation of the first block (the others are similar):</p>
<ul>
<li><code><^>!</code> means <strong>AltGr</strong></li>
<li><code><^>!6</code> means <em>AltGr + 6</em>*, pressed together</li>
<li><code>::</code> means that the input sequence has endend.</li>
<li><code>Send ~</code>: that's the AutoHotkey magic. It will send the <code>~</code> character upon receiving of this sequence.</li>
</ul>
<p>To send the backtick, I had to use the function <code>SendRaw</code> because the backtick is a reserved character in AutoHotkey.</p>
<p>I have also mapped the <em>n with tilde</em> (<code>ñ</code>) useful if you write some spanish.</p>
<h2>Execute the script (at startup)</h2>
<p>To simply execute this script, just double click on it. Now you can start writing backticks, tildes, and ñ everywhere.</p>
<p>If you want to start the script automatically at every Windows start, just put this script in this folder:</p>
<p>C:\Users$USER\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup</p>
<p>Remember to change <code>$USER</code> with your username.</p>
<p>Now Markdown will not be a problem anymore!</p>
[Risultati Sondaggio] - I programmatori e i corsi di formazione
2018-02-02T00:00:00Z
https://michelenasti.com/2018/02/02/risultati-sondaggio-i-programmatori-e-i-corsi-di-formazione.html
<p>Ciao ragazzi! Più o meno una settimana fa ho lanciato questo sondaggio nel gruppo di programmatori salernitani che ho il piacere di coordinare, <a href="https://www.facebook.com/groups/DevDaySalerno/" title="DevDay Salerno">DevDay Salerno</a>. Il sondaggio cercava di capire in che modo gli informatici professionisti si aggiornano, o comunque cercano di stare al passo con le nuove tecnologie lavorative.</p>
<p>Hanno risposto <strong>40 persone</strong>, che considero un buon numero che rappresenta la nostra community. Nel gruppo facebook ci sono circa 200 persone, su <a href="https://www.meetup.com/it-IT/devday-salerno/" title="DevDay Salerno">Meetup</a> (che è il nostro sito di riferimento, eh!) siamo 430 e passa.</p>
<p>Sarà un post con una carrellata di grafici. Pronti?</p>
<h2>Risultati</h2>
<p><img src="data:image/png;base64,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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Quanto tempo dedichi in una settimana allo studio di cose nuove? . Numero di risposte: 34 risposte." /></p>
<p>Alcuni amici mi hanno fatto notare che il blocco 3-10h poteva essere più granulare. L'anno prossimo miglioreremo!</p>
<p><img src="data:image/png;base64,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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Qual è il tuo modo preferito per aggiornarti?. Numero di risposte: 34 risposte." /></p>
<p>Sono in tanti a preferire gli <strong>articoli di blog</strong>, i <strong>tutorial on line</strong>, e altre risorse bene o male <em>gratuite</em> disponibili in rete. Niente di male: ce ne sono tante e anche di ottima qualità.</p>
<p>C'è comunque molta gente che utilizza i <strong>videocorsi</strong>, sia gratis che a pagamento, ad esempio Coursera o Udemy.</p>
<p>Tanti preferiscono <strong>conferenze</strong> o comunque <strong>incontri</strong> con persone in carne ed ossa - e se avete partecipato almeno una volta ai nostri DevDay, avreste dovuto marcare anche questa risposta :)</p>
<p>I <strong>Libri</strong> e i <strong>corsi di formazione</strong> sono meno gettonati. Forse perchè leggersi un libro è un investimento a lungo termine e che richiede tempo; mentre i corsi di formazione generalmente si pagano. <em>Si pagano quanto?</em></p>
<p><img src="data:image/png;base64,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PATRUy2WrbOzE2vWrCnBFiMiIiIiNyoWcOVeT7l3tbe3Nxci8wVcNTCqAXfTpk25XtPe3t5ciFan2dnZmQuqPp9vRk/r0NBQ7vEHDx7MheFoNIrx8fEZ05enu23bttw0gemeYZ/PNyPIExEREVH5VbwHV/SgDg0N5YYmiF7PYgLu+Pg4Nm3ahJUrV+aGOwjqNLdt24Y1a9ZYzk/+3d69e3PDItavX28ZcMVj1JKHYhARERFR+ZU94A4NDc26a4IIpjt37jQNhWbB0C7git5f+bEiRAP5e3DlC97k4CrfhkyM1V2/fv2sx4lpymN7iYiIiEiPigRcObCK8GnWs1lMD+6KFSty42zHx8dnBVx1DK6Yv9kYXBF+d+7ciVWrVuVCbmdnZy7givUSQxDU6fb29s4YskBERERElVFV98EtJuDKd1FYsWLFrCEK4h676t/c3EVBvTOCGJZgdheFlStX5i5kIyIiIqLKqelvMnOK96slIiIiqh8MuERERETkKQy4REREROQpdRFwiYiIiKh+MOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEhEREZGnMOASERERkacw4BIRERGRpzDgEpVRIpNENB1FKBVGPJNAKpvCaPQU3hntxlvDu/HHgdfwyolX8WLvFmw8sgkbDj2J1e+txf3dD+I33e3YeGQT/jiwHe+PH8TJ8CAmE0Gks2mks2lE01FMJUO6V5GoZmzbO4mbHjiGr//8AC69dR9ueuAYnt8R0L1YRFQGDLhEJRBPJxBKhpHMJDEWG8Pbo3ux8cgmtO6+B9e9fD0ueuYS/O0jZ+EDD3yoJPXhNY34xKPn4NJn58P32q146vAzODRxGPFM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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Hai mai frequentato un corso di formazione a pagamento (es. Workshop)? . Numero di risposte: 34 risposte." /></p>
<p><strong>Guardate con attenzione questo grafico</strong>. Le prossime risposte andranno ad analizzare i tre gruppi che hanno avuto a che fare con i corsi di formazione, o che desiderano farlo (amici che non volete farli, per voi il questionario finisce qui).</p>
<p>Solo il 48% di chi ha risposto al questionario ha partecipato, almeno una volta nella vita, a un corso di formazione a pagamento. Di questi, l'8,8% pensa di aver perso tempo...</p>
<h2>Risultati aggregati</h2>
<p>Le prossime domande le ho poste a <strong>tutti</strong> <strong>tranne a quelli che non hanno intenzione di partecipare a un corso di formazione.</strong></p>
<p><img src="data:image/png;base64,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" alt="" /></p>
<p>Qui vediamo tre macrogruppi, <strong>35-60€</strong>, <strong>60-100€</strong>, <strong>100-150€</strong>. Sarebbe interessante capire i prezzi "presupposti" in base al ruolo, seniority, tipo di competenze, ma poi questo sondaggio diventava caotico :)</p>
<p><img src="data:image/png;base64,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" alt="" /></p>
<p>La cosa che sorprende di questo grafico è che molti dev (più del 50% del totale) considerano giusto pagare di tasca propria i corsi di formazione. Tuttavia <strong>un abbondante 90% ritiene che le aziende dovrebbero farsi carico delle spese di formazione!</strong></p>
<h2>Segmentiamo: analizziamo le risposte di coloro che non hanno mai partecipato a nessun corso (ma vorrebbero farlo)</h2>
<p>Ricordate il grafico a torta</p>
<p><img src="data:image/png;base64,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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Pensa ad un ipotetico corso di formazione di cui avresti urgentemente bisogno. Quale pensi che sia il prezzo giusto per un corso di formazione di una giornata, con un docente specializzato, su una tecnologia moderna? . Numero di risposte: 17 risposte." /></p>
<p>La domanda completa era:</p>
<blockquote>
<p>Pensa ad un ipotetico corso di formazione di cui avresti urgentemente bisogno. Quale pensi che sia il prezzo giusto per un corso di formazione di una giornata, con un docente specializzato, su una tecnologia moderna?</p>
</blockquote>
<p>Tra coloro che non hanno mai partecipato ad alcun corso, possiamo di nuovo vedere come il tris <strong>35-60</strong>, <strong>60-100</strong>, <strong>100-150</strong> siano le fasce di prezzo più gettonate ed equidistribuite.<img src="data:image/png;base64,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XmZ92H9M6VZwRUREJMptT1HIR3WHpgnU1yvt7u6GR2nXXRYpi/DQEdzo6G15n7MtuOXh8/qTRpY3el1wy8PpuZjmZZbltLwsul1e5lDZjT55ZAquiIiIRLnNglt3oairnJ6eDpbU3d3d9OjRo5WpBmMuG1IfQKzn4ObulYv4L7/8snL7WU9RKCc/DzX9PH0hpdUndejweT6Un69XH9E9Pz+/dFlKf5faenAMXXeIgisiIiJRfv3111s9glurf8SV0uWCWxfj3KPKbjR0WaQut7Xc78qet729nd6/f79yndkW3PxpYeiXgHWJHSq4dcojvUMFNyrG+UnNE6bH/JIwU3BFREQkym1PUagNFdxyykC0nHy7dZfVxp4YIHet8tv53d3dlekUsy24Q0dIyw1azyupC270hF/3CG79icURXBEREblpbrPg1v2nPJNBLTpImJdTfoMdXVYaezarstTWR3DLTjXbObjl9IMsn/qinOycDc3BzRtw6ImKiur29val+blD0x7y0WUFV0RERK6b2z6CW3/bvO6ctfX5Z8vbjbmsPAlAfQKAaDlDP4obmtIw27MolD/2yspzvtUb8F//+tfasyjUT8a6I7H5Nru7uyuHzMspE3lQXXUYXsEVERGRKA/1l8zq8+Be112cxvW7OA9u7xRcERERifKQf6q3nCJwXbddcMujvApuwxRcERERifKQBbd1Cm7DFFwRERGJouDGFNyGKbgiIiISRcGNKbgNU3BFREQkioIbU3AbpuCKiIhIFAU3puA2TMEVERGRKApuTMFtmIIrIiIiURTcmILbMAVXREREoii4MQW3YQquiIiIRFFwYwpuwxRcERERiaLgxhTchim4IiIiEkXBjSm4DVNwRUREJIqCG1NwG6bgioiISBQFN6bgNkzBFRERkSgKbkzBbZiCKyIiIlEU3JiC2zAFV0RERKIouDEFt2EKroiIiERRcGMKbsMUXBEREYmi4MYU3IYpuCIiIhJFwY0puA1TcEVERCSKghtTcBum4IqIiEgUBTem4DZMwRUREZEoCm5MwW2YgisiIiJRFNyYgtswBVdERESiKLgxBbdhCq6IiIhEUXBjCm7DFFwRERGJouDGFNyG5YILvfny5ctDrwJMYszSIwU3puA2TMGlV8oCvTFm6ZGCG1NwG6bg0itlgd4Ys/RIwY0puA1TcOmVskBvjFl6pODGFNyGKbj0SlmgN8YsPVJwYwpuwxRceqUs0Btjlh4puDEFt2EKLr1SFuiNMUuPFNyYgtswBZdeKQv0xpilRwpuTMFtmIJLr5QFemPM0iMFN6bgNkzBpVfKAr0xZumRghtTcBum4NIrZYHeGLP0SMGNKbgNU3DplbJAb4xZeqTgxtiy7+MAAAJtSURBVBTchim49EpZoDfGLD1ScGMKbsMUXHqlLNAbY5YeKbgxBbdhCi69UhbojTFLjxTcmILbMAWXXikL9MaYpUcKbkzBbZiCS6+UBXpjzNIjBTem4DZMwaVXygK9MWbpkYIbU3AbpuDSK2WB3hiz9EjBjSm4DcsF9+vXryJdJe90RXqJMSs9RsGNKbgN+/bt24O/eERERKTdfPv27aHrSpMUXAAAZkXBBQBgVhRcAABmRcEFAGBWFFwAAGZFwQUAYFYUXAAAZkXBbdiPP/6Yfvjhh/TDDz+kP/7446FXB67022+/LcdsDrTqt99+Sz/++OOlf7fvpWVD49a+9zIFt1E///xz+vnnn1NKKR0dHQ3uhKE15biFluVCUO9b7Xtp2Zhxy18U3Eb98MMP6fT0NPx/aNFPP/2Ufvvtt4deDVjrp59+Sj/++OPgkTD7Xlq1btza916m4Dbojz/+uPTVWB7U0LJ8ZCF/RaYY0LK6KNj30oPog5l97yoFt0Gnp6d2snQnl4O8Y81fpZnDSKvqomDfSw+iD2b2vasU3AY5isBcGLe0zBFcehT9OLJk3Cq4zTIPjDmwk6Vl5uDSIwV3HAW3UT/99JNf8tKV09PTlXF6dHTkVDU0Lfqxjn0vLRuaWmPfe5mC27DyfHbf+1wa+pB3rMYtPYiOhBnDtGxo3Nr3XqbgAgAwKwouAACzouACADArCi4AALOi4AIAMCsKLgAAs6LgAgAwKwouAACzouACADArCi4AALOi4AIAMCsKLgAAs6LgAgAwKwouAACzouACADArCi4AALOi4AIAMCsKLgAAs6LgAgAwK/8PcjzW3UofkVcAAAAASUVORK5CYII=" alt="Grafico delle risposte di Moduli. Titolo della domanda: Chi dovrebbe pagare questo corso di formazione? . Numero di risposte: 17 risposte." /></p>
<p>Forse per chi non ha mai partecipato a nessun corso è il prezzo ad essere un deterrente, e vorrebbero che fosse l'azienda a pagarli.</p>
<h2>Ora analizziamo le risposte di chi ha già partecipato almeno una volta ad un corso di formazione</h2>
<p>In questo gruppo ci sono sia quelli che hanno partecipato e non gli è piaciuto, sia quelli che hanno partecipato e NON gli è piaciuto.</p>
<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAArgAAAFECAYAAADMeE3QAAAgAElEQVR4nO3de3hcZ30v+uluy3lO99n7RJS26S67BKslp4enkLYqLYdGhAYQlwRCuBgULmnaZkeBhusQYqgJUIrVBoSbxkg2ie3EeOLE2HFk+ZLEsRXha5xYlu3YsizLlnWxrJE0uoxGc/2eP7Tf8TtL6/KumTXzzix9P8/ze2xJM+t++c4771orACIiIiIiHwnongAiIiIiIi8x4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4BIRERGRrzDgEhEREZGvMOASERERka8w4Jaxuro6BALltYqqqqpQXV2tezI84ad5qRSNjY0IBALo6OjI/m7p0qUIBALo7e11fH9vb2/29YFAAM3NzcWc3LLR3NyMQCCAUCike1Ly1tHRgUAggMbGxuzvOjs7EQgE0NDQoGWaFvsxwM2+R1RpSpKexIHNrGpqatDY2IhwOFyKSako5RZww+EwqqqqUFNTU5LxmZ0Q8xEIBFBXV5fzu1LPC80zC7h1dXWoqqpSOslWV1dn12djYyPa2tqKObllQyy3Sp5fs/1Z/G7ZsmUln57FdAywOpe42ff8wux84Cfllht0KmnAFSclUcuWLcuesGpqahhyDRb7hlrMgEt6mAVcVWJ70NXaR4Xxan8m9xb7uUTm9/MB1/VVJQ24Vge2hoYGHvhMLPYNlQHXf7wIuDxOVCauP30W+7lE5vfzAdf1VWURcHt7e003us7Ozpz+djU1NQu+ohMnzN7eXixbtgxVVVUIBAKorq42/TrPOMzq6mrL6QqFQqipqbEdv5VwOIzGxsZsC3UgEMDSpUvR2dlp+vrGxsacaW9ublbaUOXWcSPxfjlMiK8DOzo6cuatoaFBqQXdOC4x/ra2tgXL36p/pNM6sOrSYlxPTtuH2TDkabdabh0dHY7bHTC/jhsaGrLzXFVVpbwcBeM2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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Pensa ad un ipotetico corso di formazione di cui avresti urgentemente bisogno. Quale pensi che sia il prezzo giusto per un corso di formazione di una giornata, con un docente specializzato, su una tecnologia moderna? . Numero di risposte: 13 risposte." /></p>
<p>La prima domanda è la stessa del gruppo precedente: quanto vorresti pagare? Sorprendentemente qui c'è un vincitore, anche se non maggioritario: il gruppo dei <strong>100-150€</strong>.</p>
<p><img src="data:image/png;base64,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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Chi dovrebbe pagare questo corso di formazione? . Numero di risposte: 13 risposte." /></p>
<p>Anche in questo mondo "ideale", sono in parecchi a ritenere che l'azienda dovrebbe sponsorizzare questi corsi.</p>
<h3>Ora vediamo le risposte sull'ultimo corso a cui hanno partecipato</h3>
<p><img src="data:image/png;base64,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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Pensa all'ultimo corso di formazione a cui hai partecipato. Quanto hai speso? . Numero di risposte: 12 risposte." /></p>
<p>Si, avete letto bene: il 33,3% di chi ha partecipato almeno ad un corso lo ha fatto a un prezzo di 200€ o più.</p>
<p><img src="data:image/png;base64,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BQDIdK+HKLx79+5KwX337l1zeXm5+P83b9407969u/K7X79+bV6+fLnY29s0TfPq1avFab99+9a8fPmymc1mV0rybDZr3r592zx//vzKXuLLy8vmzZs3zWw2a2azWfPmzZul61N+bzabNa9fv24uLy+bd+/eLU4/m82ar1+/Nk3TNJ8+fWpevHjRzGaz5tWrV823b99ue5cBALCm0Qtu27dv35rnz58vCmNb2WP64cOHxc/bBffly5eLwxXKacvxueX3ys+eP3/efPr0qWmaZrFn+fLysrm8vGxevny5KMClsJafvX79unnz5s3SZRQXFxdLZfft27crbysAAJsRUXBLWSx7UPt8/fr1yu92C+6bN28We02/fv262BPbPZTg7du3i8uZzWZLe1q/ffu2OP2nT58WZfjy8rK5uLhYOv/2+b57927pul9eXjaz2WxpbzAAAJsXUXCLi4uLpT2xbdcV3IuLi+bNmzfNixcvmhcvXiwd5tAtuOV61I6jbf/bn3/+uTgs4vXr19WCW07TTd/eaAAANmfUgluOZ111mmJVwb28vFwqkuUwhFJGr9uD2/6qsXZxbX8NWTlW9/Xr11dOV85znW+AAABgM0bfg/v8+fOlvbAvX76sfshs1R7c58+fL/b8XlxcXCm43WNwSyHuOwa3lN8PHz40L1++XJTct2/fLgpuOZShHILQPd+vX79WD7cAAGBzRi+4Q78H97qC2/4WhXZpbpp/vkWh72frfItC95sRyvXu+xaFFy9eLD7IBgDA/dnqv2Q2lO+rBQD4eSi4AABMioILAMCk/BQFFwCAn4eCCwDApCi4AABMioILAMCkKLgAAEyKggsAwKQouAAATIqCCwDApCi4AABMioILAMCkKLgAAEyKggsAwKQouAAATIqCCwDApCi4AABMioILAMCkKLgAAEyKggsAwKQouAAATIqCCwDApCi4AABMioILAMCkKLgAAEyKggsAwKQouAAATIqCCwDApCi4AABMioILAMCkKLjBfvz40Xz//l1ERESkNz9+/Bi7rkRScIN9//69+de//jX6k0fq+fvvv0e/DmI+2xwzyo75ZKfMh6sU3GDfv/9/wSXX33//PfZVYAXzyWdG2cwnm4Jbp+AGU3DzWfyzmU8+M8pmPtkU3DoFN5iCm8/in8188plRNvPJpuDWKbjBFNx8Fv9s5pPP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" alt="Grafico delle risposte di Moduli. Titolo della domanda: Chi ha pagato l'ultimo corso di formazione a cui hai partecipato?. Numero di risposte: 13 risposte." /></p>
<p>Finalmente la realtà viene a galla: la maggior parte degli intervistati ha pagato questi corsi di tasca propria.</p>
<p>Questo dato potrebbe essere interpretato in molti modi, ad esempio che <em>le aziende non investono</em> o che <em>i developer ci tengono alla loro formazione più dei loro datori di lavoro...</em> ma lascio questo tipo di commenti a voi.</p>
<h3>E io?</h3>
<p>Ho voluto verificare un attimo quale fosse il <em>sentiment</em> della community su questo argomento, giusto per capire se se ne sente il bisogno, se siamo "nati imparati", se è una questione di prezzo, etc.</p>
<p>Io non ho partecipato a questo sondaggio, perchè non volevo falsare i dati. A futura memoria vi dico che <em>ho partecipato ad un solo corso di formazione</em> in passato, e me lo sono pagato io. All'epoca l'ho fatto perchè l'argomento era di nicchia (NodeJS, agli inizi) e non trovavo intorno a me persone che avevano una conoscenza professionale della materia.</p>
<p>Ne farei altri <strong>se non dovessi chiedere le ferie per potervi partecipare</strong> (in passato era così, oggi non più), mentre sarei disposto anche a pagare di tasca mia se le competenze da acquisire riguardano me e non quello che devo fare in azienda.</p>
<p>Una volta ho trovato una citazione su un sito americano, che purtroppo non trovo più: <strong>un informatico che non si aggiorna almeno 8 ore alla settimana è un informatico che nel giro di 5 anni sarà fuori mercato</strong>. (Aiutatemi a ritrovare la citazione! Conteneva numeri, dati, fonti.)</p>
<p>Informatico avvisato...</p>
Un giro nell'universo React
2018-02-18T00:00:00Z
https://michelenasti.com/2018/02/18/un-giro-nell-universo-react.html
<p>Da quando <strong>React</strong> è stato rilasciato da Facebook è diventato rapidamente una delle librerie più usate per la realizzazione di interfacce web. Come mai?</p>
<p><img src="https://michelenasti.com/images/1024px-React-icon.svg.png" alt="" title="React Logo" /></p>
<h2>Perchè React piace ai millenials</h2>
<p>Abbiamo iniziato a sentir parlare di React in un momento storico in cui AngularJS 1.x era al massimo del suo hype, e si iniziava a progettare Angular2. Quei fantastici ragazzotti della Silicon Valley quando generano tanto hype in genere lo fanno perchè c'è un motivo, ossia ci vedono lungo e in effetti React è con noi da ormai tre anni, che è tantissimo per una libreria frontend.</p>
<p>I motivi per cui piace sono:</p>
<ul>
<li><strong>E' una libreria che si occupa solo della view</strong>, quindi tutto il resto è lasciato nelle mani del programmatore. Penso a: comunicazione col server, traduzioni, o più semplicemente gestione dello stato. Per ognuna di queste cose si possono usare librerie esterne (fatte molto bene).</li>
<li>A parte una curva d'apprendimento "media", tutto sommato <strong>React è abbastanza semplice da imparare e usare</strong>.</li>
<li>Ma quali sono i vantaggi rispetto a Angular1? (Lo confronto con questo framework perchè, all'epoca in cui fu rilasciato React, c'era solo lui.) Quello che nessun programmatore ammette di amare, ma che invece è un gran vantaggio, è <strong>il <em>live preview</em> dei cambiamenti</strong>, sfruttando le magie di webpack e dell'hot module reloading, per cui i tempi di sviluppo si riducono enormemente.</li>
<li>Inoltre <strong>React gestisce i cambiamenti di migliaia di componenti su una sola pagina senza appesantire il browser</strong>, anzi è estremamente veloce nel capire cosa è cambiato e cosa deve ri-renderizzare.</li>
<li>E' <strong>sponsorizzato da FB, è utilizzato in produzione da tante startup di grande successo</strong> (tra cui AirBnB) e questo ha contribuito all'hype.</li>
</ul>
<p>La mia paura più grande era di dover imparare un mix di tecnologie che non conoscevo: jsx, webpack, es6... Da create-react-app in poi tutto questo è una bestia "addomesticabile".</p>
<h2>L'uso di ES6</h2>
<p><strong>React può essere usato anche con ES5</strong>, ossia la versione precedente di Javascript che non aveva tante feature, e a dirla tutta può addirittura essere usato senza JSX, che sarebbe un modo per scrivere HTML dentro JS. All'atto pratico tutto ciò è infattibile, <strong>i benefici di JSX (e di babel/webpack che compilano/trasformano) sono impagabili.</strong></p>
<p>Ma è con <strong>ES6</strong> (la versione di js che contiene arrrow functions, destructuring, etc.) che React viene di solito scritto/usato, ed è quindi con ES6 che troverete codice, esempi e documentazione. Da convinto programmatore NodeJS, la sintassi che più detesto è <code>import Something from './something'</code> e la corrispettiva <code>export const</code>. Comunque, ci si abitua in fretta.</p>
<h2>React vs Vue</h2>
<p>Ci sarebbe tanto da dire, ma voglio concludere ripetendo che questo post viene da neanche un mese di React quindi le mie opinioni potrebbero cambiare.</p>
<p>Io conosco abbastanza bene anche VueJS, e sinceramente trovo anche questo framework fatto bene con tante feature e una logica di fondo molto chiara, per cui bisogna fare meno scelte rispetto a React (es. come gestire lo stato, come strutturare i componenti ...) ma React ha uno sponsor più grande e questo garantisce, sulla carta, una vita più lunga.</p>
<p>E voi che esperienza avete? Se volete scrivete le vostre impressioni nei commenti, saranno sicuramente utili a chi passa per questo blog e cerca informazioni.</p>
Consigli ai junior e ai laureandi (ossia: evitate gli errori che ho fatto io)
2018-02-25T00:00:00Z
https://michelenasti.com/2018/02/25/cosa-ho-imparato-dai-miei-sbagli-ossia-consigli-per-i-giovani-laureandi.html
<p>Ieri sera abbiamo tenuto un altro incontro del <a href="https://www.meetup.com/it-IT/devday-salerno/?chapter_analytics_code=UA-74462208-1">DevDay Salerno</a>, un gruppo di programmatori che si riunisce più o meno una volta al mese per parlare di programmazione, lavoro, informatica, etc. E' andato molto bene.</p>
<p>All'incontro c'erano anche due studenti in cerca di uno stage e ciò mi ha fatto molto piacere: la community si è mostrata molto ricettiva con loro.</p>
<p>Prima dell'incontro <strong>uno dei due mi ha inviato il cv affinchè gli dessi uno sguardo</strong>. Purtroppo la mia risposta non è stata quella che si aspettavano: "<em>fa schifo</em>".</p>
<p><img src="https://michelenasti.com/images/0402ae2.jpg" alt="" /></p>
<h2>Che hanno i loro CV che non va?</h2>
<h3>Regola numero 1: <strong>realizza il CV nel formato che vogliono le aziende.</strong></h3>
<p>In Italia vuol dire, al 99%, <strong>formato europeo</strong> (potete dire che fa schifo, che è verboso, ma le aziende vogliono questo e questo dovete dargli). Il loro CV invece era colorato, sfondo verde, icone, etc.</p>
<p><strong>Se si cerca lavoro all'estero non basterà tradurre il cv</strong>: gli stranieri sono abituati a un formato più snello accompagnato da cover letter, quindi dovrai realizzare un CV anche così.</p>
<p>Io ne ho due sempre pronti e sempre aggiornati, uno <em>formato Europeo</em> (...che all'estero non usano) e uno in Inglese, per le aziende internazionali.</p>
<h3>Regola numero 2: <strong>Keep it professional</strong>.</h3>
<p>Come ho già scritto il CV era colorato, con addirittura il <em>simboletto di whatsapp accanto al numero di telefono</em>. <strong>Un CV del genere sarebbe appena valutato (...forse?) da un'agenzia di comunicazione</strong>, ma io non lo consiglierei e infatti gli ho intimato di ritornare al bianco & nero. Ricordiamoci che il CV serve per trovare lavoro, quindi non dobbiamo mettere tutti i possibili modi per trovarci (twitter... facebook... msn?!).</p>
<p>Alcune <strong>eccezioni</strong> a questa regola:</p>
<ul>
<li><strong>Un blog</strong>. Se hai un blog con contenuti relativi alla tua professione, <strong>il blog merita di essere in prima pagina proprio sotto al numero di telefono</strong>. I recruiter infatti ti cercano sui social, e se gli rendi facile l'accesso al tuo sito è come se il colloquio partisse già in discesa. Il mio blog è in prima pagina ;)</li>
<li><strong>Eventuali altri contatti e riferimenti social SOLO se richiesti.</strong> Per posizioni di copywriting, o altre di cui non ne ho idea, potrebbe essere necessario fornire anche il proprio profilo social. In questi casi valutate voi se darli oppure no (purtroppo ho visto job descriptions in USA in cui cercavano dev con un <a href="https://klout.com/#/micnasti"><em>Klout Score</em></a> di un certo tipo).</li>
</ul>
<p><strong>Prima tenevo molto alla privacy ma poi ho capito che i social potevano essere usati a mio favore</strong>. La mia bacheca prima era chiusa ai più, poi ho cambiato completamente registro e <strong>adesso condivido prevalentemente roba di lavoro</strong>; chi mi trova sa cosa ha trovato. E il colloquio si semplifica parecchio, di nuovo.</p>
<h3>Regola numero 3: <strong>non mettere la lista degli esami</strong></h3>
<p>Ovviamente, se te la chiedono (per alcuni lavori statali la lista degli esami è richiesta), puoi inserirla. Ma in generale non ha senso.</p>
<p>Il problema è che la lista degli esami è un'arma a doppio taglio: compare sicuramente anche quel voto di cui ti vergogni, o l'esame che hai <em>rubato</em>, ma soprattutto la lista degli esami non fa capire a un selezionatore se davanti ha una persona che gioca di squadra, o una persona orientata alla ricerca, al project management, etc.</p>
<p>Questo è un errore che ho commesso anche io, e <strong>ricordo l'imbarazzo quando il selezionatore di IBM me lo fece notare</strong>.</p>
<p><strong>Cosa mettere, dunque, in questo CV?</strong> Chi si laurea, magari alla triennale, non ha idea di cosa scriverci.</p>
<p>Durante i tre anni di università si sviluppano tanti progetti: io ho addirittura sostenuto un esame come project manager, cosa che si è poi rivelata molto utile nel mondo del lavoro e come soft skill. Conviene parlare di questi progetti nel CV: racconta cosa hai fatto, il tuo ruolo, le tecnologie usate.</p>
<p><strong>Se avete progetti extrauniversitari</strong> (es. siti web, app, etc) che hanno un senso per chi vi intervista, <strong>inseriteli</strong>.</p>
<p><strong>Se invece avete fatto i camerieri</strong> (come me!), io l'ho scritto sotto una voce "altre esperienze", ma è davvero scritto così: <em>altre esperienze: cameriere</em>. Se ti stanno intervistando per il ruolo di front-end developer, a chi serve sapere che sai portare le pizze ai tavoli? Può però essere utile sapere che non sei stato con le mani in mano, ma questo dipende dalle opinioni dei selezionatori.</p>
<h2>Come procurarsi un paio di colloqui senza inviare nemmeno un CV</h2>
<p>Il trucco è davvero semplice e stupido: i due ragazzi di cui sopra sono venuti al nostro evento, si sono presentati (sotto mio consiglio) a decine di persone spiegando chiaramente che erano in cerca di stage.</p>
<p>Ovviamente <strong>ieri sera non c'erano i CEO</strong> (...in verità qualcuno si), ma <strong>una sovrabbondanza di senior programmer e team leader che hanno fatto un colloquio "al volo" al termine dell'evento</strong>, tra una pizzettina e una coca.</p>
<p>Un altro consiglio è di parlare con tutti, anche con quelli che non ti interessano (specialmente ora che non hai nessun tipo di esperienza), e di accettare il lavoro che ti permette di imparare di più.</p>
<p>Ecco infatti un'altra cosa che ho imparato sulla mia pelle: <strong>rifiutare un lavoro non significa rifiutarlo per sempre</strong>, dobbiamo infatti lavorare 35 anni (almeno) e chi sa che non ci si reincontra, in questo lavoro o nel prossimo.</p>
<p>Una cosa che infatti i laureandi sottovalutano è che fare una buona <em>prima esperienza</em> è fondamentale: <strong>conosci delle persone che poi saranno mentor per tutta la tua vita</strong>, che ti indirizzeranno anche in base alle tue inclinazioni. Non mento se vi dico che le persone che ora considero cruciali per la mia carriera professionale le ho trovate alla mia prima esperienza.</p>
<h2>E non abbiamo parlato di soldi</h2>
<p>Finchè siete junior, <strong>non proccupatevi dei soldi</strong>. Non sto dicendo che devi morire di fame, anzi devi guadaganare il giusto per vivere (non sopravvivere), ma i soldi veri li farai quando ti riterrai senior e potrai far pesare le tue capacità all'azienda.</p>
<h2>Aiutiamo i junior!</h2>
<p>Mi piacerebbe leggere nei commenti il tuo consiglio per junior, laureandi e neolaureati. <strong>C'è un esercito di studenti che sta per terminare gli studi e vuole risposte a domande che non sa porre</strong>. Aiutiamoli!</p>
Come realizzare un form esterno (Es. assistenza) che manda dati a MagNews
2018-04-05T00:00:00Z
https://michelenasti.com/2018/04/05/come-realizzare-un-form-esterno-es-assistenza-che-manda-dati-a-magnews.html
<p>Questa roba l'ho dovuta fare per lavoro e, nello scoprire tutti i passaggi necessari, me li sono segnati.</p>
<h2>Cos'è MagNews? (Spoiler: non lo so)</h2>
<p>E' un tool per il marketing automation, email marketing, roba così.</p>
<p>Se siete qui probabilmente già sapete cos'è e avete anche un account.</p>
<h2>Cosa vogliamo fare</h2>
<p>Su un sito creiamo una pagina "contattaci" in cui l'utente inserisce tutti i dati necessari; questi dati piuttosto che andare in un backend qualsiasi vanno a finire su MagNews.</p>
<p>Passiamo all'azione!</p>
<h3>Creare il form su Magnews</h3>
<p>Dalla dashboard di MagNews, <em>Design -> Siti Web -></em> Selezionate il sito su cui volete operare -> <em>crea nuova pagina di tipo "forms"</em>.</p>
<p>Disegnate un form che contiene i dati che vi interessano. Non è importante la grafica, ma solo che siano presenti i campi di cui avete bisogno.</p>
<p>Per ogni campo, dategli un <strong>nome</strong> e poi in <em>Avanzato -> Opzioni per gli script -></em> date anche un <strong>NAME del campo</strong><em>.</em> E' importante che questo nome sia <strong>tutto minuscolo</strong>.</p>
<p>Create anche un tasto "submit" a cui dovete dare le solite info, come se fosse un campo.</p>
<p>Primo step superato :)</p>
<h3>Creare form HTML</h3>
<p>Questa è la parte facile, dovete realizzare il form nel modo che più vi piace nella pagina che volete voi.</p>
<p>Ma ora dobbiamo agganciare un evento al tasto SALVA.</p>
<h3>Comunicazione JS / MagNews</h3>
<p>Secondo la loro <a href="https://be-mn1.mag-news.it/be/documentation.do?packageid=advanceduserguide&pageid=ws023">pagina ufficiale per i devlopers per creare i FORM</a>, i dati devono essere in questo formato:</p>
<p>{
"idform":432,
"parameters":[
{
"name":"campo_di_testo_1",
"value":"123"
},
{
...
},
{
"name":"submit_button", // auto generated input name
"value":"I am an input value"
}
]
}</p>
<p>Cerchiamo di mettere insieme i pezzi.</p>
<p>La prima cosa che vi serve è un URL a cui mandare i dati.</p>
<p>Io l'ho trovata cliccando su <em>Visualizza Codice Form</em>, mi è comparso il form html costruito fino a quel momento. (Esempio: <code>http://tuosito.mno08.com/nl/tuosito_page543.mn</code> ).</p>
<p>Sempre secondo la documentazione ufficiale, dobbiamo cambiare leggermente questo URL e trasformarlo in <code>http://tuosito.mno08.com/nl/api/forms</code>.</p>
<p>Dopodiché ci serve l'ID del form per il parametro <code>idform</code>. Anche questo l'ho trovato nella "mappa del sito" su MagNews; di fianco alla pagina c'era l'id tra parentesi graffe.</p>
<p>Infine, i campi. Se avete settato tutto bene, dovete inviare tutti i campi specificati più il bottone submit (wtf?!) con un valore a caso.</p>
<p>Se tutto va bene vedrete i vostri dati salvati in MagNews (sempre dal backoffice, andate in edit sulla pagina che contiene il form e cliccate su <em>Visualizza Risultati</em>).</p>
<p>Bene, spero di avervi tolto le castagne dal fuoco! Io invece mi sono scritto questo post per ricordarlo, casomai dovesse servire ancora.</p>
Un programmatore dovrebbe continuare a programmare nel suo tempo libero?
2018-04-05T00:00:00Z
https://michelenasti.com/2018/04/05/un-programmatore-dovrebbe-continuare-a-programmare-nel-suo-tempo-libero.html
<p>Programmare è diventata una passione.</p>
<p>Questa frase mi suona ridicola, perchè io non sono mai stato un tipo che realizza side projects, neanche ai tempi dell'università (e sbagliavo!).</p>
<p>Ciò che mi piace, della programmazione in se e per se, <strong>è il processo mentale che parte prima di scrivere la prima riga di codice</strong>: "<em>se succede questo faccio questo... e se succede quello faccio quello.... nel browser questo non si può fare quindi lo simulo così....</em>"</p>
<p>Specialmente durante la prima fase lavorativa (post-università) ricordo serate che tornavo a casa dopo una giornata di lavoro e mi rimettevo col computer a fare prove, seguire tutorial, corsi, etc.</p>
<p>Poi è nata mia figlia e il tempo di accendere il computer (o di fare qualsiasi altra cosa, come suonare la chitarra) non c'è stato più.Dopo un anno per fortuna la bimba sta iniziando ad andare a dormire alle 21:30 (e mia moglie segue programmi TV che non mi piacciono) quindi ho parzialmente ritrovato tempo per il mio "hobby".</p>
<h2>Ma: un buon programmatore dovrebbe continuare a programmare, nel suo tempo libero?</h2>
<p>Come al solito, dipende.</p>
<p><strong>Se si torna a casa e si continua a lavorare</strong>, <strong>potrebbe essere divertente ma probabilmente non è un hobby.</strong></p>
<p>Se dovessi rileggere il discorso scritto prima e mettergli la firma di qualcun altro, penserei che chi l'ha scritto deve essere uno di quegli <em>azzeccati</em> che passano il tempo a fare sempre la stessa cosa, programmare programmare programmare - <em>probabilmente è anche un tipo spocchioso, fastidioso, saccente, buffone</em>...</p>
<p>Se invece dovessi fare un colloquio di lavoro a una persona che apre con queste parole, penso gli farei qualche domanda più tosta per testare le sue capacità analitiche.</p>
<p><img src="https://michelenasti.com/images/rtryt.jpg" alt="" /></p>
<p>Ormai lavoro da qualche annetto e qualche programmatore l'ho conosciuto durante la mia carriera; i colleghi si dividono in:</p>
<ul>
<li><strong>Lavoro solo per lo stipendio</strong>: La programmazione non mi interessa, alle 18 voglio stare in viaggio verso casa. Conosco abbastanza bene solo gli strumenti che mi servono per lavorare, di sicuro non propongo io qualcosa di nuovo al mio team.</li>
<li><strong>L'intelligenza non mi manca, ma fondamentalmente sono un tipo pigro</strong>: queste persone capiscono tutto al volo, leggono tanto, non provano nulla. A casa dopo il lavoro preferiscono netflix.</li>
<li><strong>Voglio stare sempre una spanna avanti a tutti</strong>. Questa è la categoria più pericolosa, perchè studiano <em>anche</em> per passione, ma il secondo fine neanche tanto celato è quello di fare carriera e di dimostrare la propria superiorità.</li>
<li><strong>Mi piace programmare ma al tempo stesso riesco ad avere una vita sociale equilibrata</strong>: scherzavo ragazzi, non esiste nessuno in questa categoria. O siete nerd o non siete niente 😂</li>
</ul>
<p>Citiamo un'altra categoria che ho contribuito a creare, nel mio piccolo, a Salerno e in Campania. Quella dei <strong>programmatori consapevoli</strong>. Da quando ho fondato il <a href="http://www.devday.it/">DevDay</a> ho intercettato un sacco di professionisti che, per motivi privati o familiari, non hanno più il tempo di scoprire cose nuove. Q<strong>ueste persone vogliono aggiornarsi, ma non possono rubare tempo alla cosa più bella che hanno, ossia la loro vita.</strong> E quindi "consacrano" un sabato al mese per venire agli incontri che organizziamo, i DevDay appunto, per scoprire le ultime novità o semplicemente incontrare altre persone appassionate. Queste persone diventano anche molto focose se gli tocchi il loro framework preferito, ma <strong>generalmente sono persone che amano ascoltare le opinioni degli altri. Esattamente il contrario delle community on line :)</strong></p>
<h2>Programmando si invecchia</h2>
<p>Avete mai conosciuto programmatori over-40? e over-50? Io si, ma si contavano sulle dita di una mano. La stragrande maggioranza degli over-40 o over-50 in realtà occupava posti manageriali (capo team, responsabile di progetto, etc.).</p>
<p>Non tutti possono diventare capi, o manager. <strong>Che fine fanno i programmatori dopo una certa età?</strong></p>
<p>Quando un giorno la programmazione sarà fonte di noia anche per me, in che modo trasformo la mia vita affinchè non sia completamente buttata?</p>
<p>A queste domande non so ancora rispondere. Se ti piace fare qualcosa vorresti poi farla per più tempo possibile. Salvo poi scoprire che farlo tutti i giorni, e anche nel tempo libero, si rivela non essere questo gran divertimento. E insomma devi necessariamente "scappare" dal codice per passare ad altro, altrimenti rischi di passare la tua giornata a fare qualcosa che odi.</p>
<p>E questo non lo auguro a nessuno!</p>
Ode ai libri di carta
2018-04-16T00:00:00Z
https://michelenasti.com/2018/04/16/ode-ai-libri-di-carta.html
<p><img src="https://michelenasti.com/images/kindle-vs-books.jpg" alt="" /></p>
<p>Ci ho provato, attratto dall'idea di portarmi un solo dispositivo dietro con tutti i libri che volevo leggere.</p>
<p>Ho provato anche altre varianti, tipo il <em>kindle</em> (che non trovo più!), il <em>tablet</em> tradizionale, il <em>phablet</em>.</p>
<p>Leggere su questi dispositivi è scomodo, stancante, e soprattutto impossibile. <strong>Sono continuamente distratto da altro.</strong> Non ricordo nemmeno di rimettere la suoneria quando mi sveglio, figuriamoci se ricordo di disattivare le notifiche quando leggo!</p>
<p>E mentre attendo di completare il libro sullo <a href="https://leanpub.com/taming-the-state-in-react">state management in React</a>, libro piacevolissimo per carità, ma comunque digitale, la pila di libri di carta sul mio comodino cresceva insieme ai sensi di colpa.</p>
<p>Così ieri sera sono tornato alla carta, con l'obiettivo di trovare una strategia per finire i due libri digitali e non comprarne mai più.</p>
<p>Sono consapevole che prendere tutto di carta significa comprare un mucchio di roba (tecnica) che nel giro di pochi mesi potrebbe risultare obsoleta.</p>
<p>Dopo aver sperimentato il problema, sono giunto alla conclusione che <strong>il miglior supporto è quello che scompare per lasciare spazio al contenuto.</strong></p>
<p>E voi cosa preferite? libri o digital?</p>
Cambia la tua vita lavorativa! (partecipando a uno Startup Weekend)
2018-04-22T00:00:00Z
https://michelenasti.com/2018/04/22/startup-weekend-salerno-2018.html
<p>Avete la vostra lista di cose nuove da fare ogni anno? Io sinceramente no, ma se l'avessi probabilmente per quest'anno avrei scritto: <strong>partecipare a uno startup weekend</strong>.</p>
<p>l'occasione l'ho avuta già l'anno scorso ma é sfumata a causa di un altro impegno; la curiosità è rimasta e quindi quest'anno ho riservato lo slot: ho annunciato con largo anticipo a moglie e familiari che mi sarei dedicato a quest'esperienza.</p>
<h2>Cos'é uno startup weekend</h2>
<p><strong>È un weekend - 54 ore - in cui si lavora intensamente ad un'idea di business</strong>.</p>
<p>Tutti abbiamo idee che sembrano geniali, ma come fare per sapere se possono avere successo o se sono delle cagate pazzesche?</p>
<p>In uno startup weekend ci si confronta con coach, mentor, altri partecipanti e l'idea passa attraverso tre fasi:</p>
<ul>
<li><strong>scoperta</strong>: l'idea "acchiappa" altri partecipanti, che magari sono venuti senza un'idea propria ma hanno sentito il tuo discorsetto e ti hanno trovato interessante.</li>
<li><strong>analisi</strong> del mercato potenziale, dei problemi, dei rischi: attraverso il <strong>business model canvas</strong> (uno strumento che viene presentato durante il weekend) le idee iniziano a farsi più chiare e si riesce a rispondere anche a domande cruciali, tipo: che problema sto risolvendo? A chi? Come ci farò i soldi?</li>
<li><strong>pitch</strong>: senza lilleri non si lallera, ed é per questo che ci si prepara ad uno speech per convincere un investitore a sganciare soldi motivati.</li>
</ul>
<p><img src="https://michelenasti.com/images/IMG_20180422_132054.jpg" alt="mentors, organizzatori, staff, etc etc mentre è in posa per la foto ufficiale (non la mia eh) " title="Staff Startup Weekend Salerno 2018" /></p>
<h2>Il processo</h2>
<p>Dal punto di vista di un mentor <strong>vedi le idee/cazzate che diventano, lentamente (e faticosamente) progetti di business rispettabili</strong>: siamo passati da chi voleva fare un'app per combattere la noia a un serio progetto per concentrarsi durante lo studio, o da un'app per viaggi tra sconosciuti ad un'app per organizzare viaggi di gruppo con persone che hanno qualcosa in comune; e ancora: un modo prendersi cura di un animale anche se purtroppo non puoi più tenerlo in casa; un tracking real time per autobus che non lo hanno, e un'app/gioco in cui più cammini, più guadagni. Queste sono solo alcune delle idee che ho avuto il piacere di accompagnare.</p>
<h2>conta il viaggio, non la destinazione</h2>
<p><strong>Con i ragazzi ci sono stati scontri anche duri</strong>, volti soprattutto a fargli notare le contraddizioni dei loro ragionamenti. Una persona non scarica un'app "perché é annoiato", oppure un'autolinea di autobus non implementa il tuo sistema di tracking se non gli porti un vantaggio che altri competitor non hanno...</p>
<p>Con la scusa delle loro idee <strong>i ragazzi hanno acquisito strumenti, come il business model canvas e l'arte del pitching</strong>, che potranno riutilizzare per i progetti futuri.</p>
<p>Abbiamo quindi creato futuri milionari? No, a parte una startup che ha validato l'idea sul posto e ha guadagnato i suoi primi 50€ non abbiamo mosso grandi cifre; <strong>ma abbiamo sicuramente dato a dei ragazzi gli strumenti per farsi le domande giuste.</strong></p>
<p>Peccato che questi eventi siano nati molto dopo il mio periodo universitario: <strong>quando ero studente non c'era NIENTE di tutto questo</strong>, sarebbe stato davvero figo parteciparvi. Conserverò il ricordo della gioia e l'adrenalina di vedere i ragazzi sul palco!</p>
Proteggi le tue applicazioni Express con HelmetJs
2018-05-05T00:00:00Z
https://michelenasti.com/2018/05/05/proteggi-le-tue-applicazioni-express-con-helmetjs.html
<p>Quanto ne capisci di sicurezza informatica? Io molto poco, anzi a dire il vero trovo l'argomento addirittura noioso. Questo non vuol dire che lo sottovaluto: è una di quelle cose che sì, è una palla, ma sì, bisogna trattare l'argomento <em>seriamente</em>.</p>
<p><img src="https://michelenasti.com/images/testuggine-8.jpg" alt="Testuggine romana" title="Metti questi tizi a proteggere il tuo server " /></p>
<p>Intorno al mondo della sicurezza informatica esistono addirittura delle figure specifiche (il pentester, ad esempio, che per lavoro deve <em>bucare</em> i server dei suoi clienti e trovare eventuali falle - per farle correggere!) o comunque interi team devoti alla sicurezza, che si occupano di implementare patch, conoscere gli attacchi più diffusi, capire cosa sta accadendo in tempo reale nella loro rete, etc.</p>
<p>Tutto questo preambolo per dire: quando realizzi la tua applicazione web <strong>pensi alla sicurezza?</strong> Se usi NodeJS è molto probabile che hai scritto qualcosa con Express; <strong>ti sei mai preoccupato di sapere se l'app che hai rilasciato in produzione è <em>sicura</em>?</strong></p>
<p>Ripeto, la sicurezza è una cosa seria e non va abbozzata. Basta installare wordpress pulito su un server custom per scoprire dopo neanche dieci giorni che è stato bucato. Se siete un po' come me, è probabile che a tutto questo ci pensate alla fine (prima vi rimboccate le maniche per fare in modo che il servizio <em>funzioni</em>). Come evitare di prendere sottogamba l'argomento?</p>
<p>L'ideale, per me, è di trovare un middleware per Express che lo agganci e - <em>magia! -</em> la tua app è più sicura.</p>
<p>Tale progetto esiste e si chiama <a href="https://helmetjs.github.io/">HelmetJS</a>.</p>
<p><img src="https://michelenasti.com/images/helmetjs.png" alt="Helmet Logo " title="Il logo di HelmetJS. Purtroppo esiste solo in bassa qualità" /></p>
<h2>Come funziona</h2>
<p><strong>HelmetJS</strong> non fa analisi di ciò che entra o ciò che esce dal tuo sito.</p>
<p>Quello che invece fa è aggiungere una serie di best practices a Express, alcune che sono semplici regole dettate dal buon senso, e altre che invece dipendono dal contesto.</p>
<p>Un esempio di regola semplice: rimuvere il tag <code>X-Powered-By</code> che di default Express setta con nome e versione; questo rende la vita facile a un attacker che può trovare un exploit specifico per il tuo server.</p>
<p>Altri esempi li trovate cliccando sui vari <a href="https://helmetjs.github.io/#reference">link dei sottomoduli</a> di Helmet, installabili singolarmente.</p>
<h2>Come si installa</h2>
<p>niente di più semplice, e a me le soluzioni semplici piacciono.</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> <span class="token function">install</span> helmet <span class="token parameter variable">--save</span> </code></pre>
<p>Dopodichè si tratta solo di istanziare Helmet nel vostro server e usare il middleware il più in alto possibile:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><br /><span class="token keyword">var</span> helmet <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'helmet'</span><span class="token punctuation">)</span><br /><br /><span class="token keyword">var</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><br />app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">helmet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br /><br /><span class="token comment">// ...</span></code></pre>
<h3>Mai più preoccupazioni?</h3>
<p>Non scherziamo. Gli hacker ne sanno una più del diavolo, e non appena un software - sistema - applicativo si diffonde, sfruttano bug sempre più complessi.</p>
<p>Però bisogna riconoscere che Helmet è un progetto semplice che fa quelle cose che Express avrebbe dovuto fare di suo. Per me, è un <em>mai più senza</em>.</p>
<p>Vi lascio le mie personali regole non scritte (prima d'ora) della sicurezza informatica:</p>
<ul>
<li>più una cosa è semplice, più è difficile da bucare</li>
<li>aggiorna sempre tutti i sw, e se non puoi iscriviti alle mailing list di sicurezza dei software che tieni in produzione (così avvisi chi di dovere)</li>
<li>Se i dati che gestisci sono importanti, assumi uno che ne capisce</li>
</ul>
<p>Conoscete tool simili a Helmet ma per altri linguaggi? Se si, indicali nei commenti!</p>
[libri] Sei proprio il mio typo: la vita segreta dei caratteri tipografici
2018-06-05T00:00:00Z
https://michelenasti.com/2018/06/05/libri-sei-proprio-il-mio-typo-la-vita-segreta-dei-font.html
<p>Questa è la storia di un libro che ho comprato due volte.</p>
<p>La prima volta purtroppo l'ho perso in aereo, dopo un primo capitolo che raccontava <a href="https://michelenasti.com/2017/12/01/batman-ha-ispirato-il-comic-sans-storia-di-un-font-che-tutti-odiano.html">la storia del Comic Sans</a> (puoi leggere un mio riassunto al link precedente!).</p>
<p>L'ho quindi ricomprato e me lo sono finito, scoprendo <strong>la storia segreta dei font.</strong></p>
<p><iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-eu.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=ilblodimicnas-21&o=29&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=B00QFIVE8I&linkId=c330ea265a0ba9192f74f16adb252a00"></iframe></p>
<p>Già, i font: su questo blog uso un (o una?) font (<a href="https://fonts.google.com/specimen/Libre+Baskerville">Libre Baskerville</a>), ossia un insieme di disegni che compongono i caratteri, realizzati da artisti di tutto il mondo che spesso non vedono riconosciuto il loro lavoro.</p>
<p>I primi font risalgono addirittura a Gutemberg, e in passato <strong>realizzare un font era un lavoraccio da fonderia</strong>: oltre alla parte dei disegni, bisognava poi fondere il metallo per realizzare i singoli caratteri in tutte le forme e misure. Le fonderie/tipografie poi rivendevano questi stampi a chi volesse utilizzarli per i propri progetti!</p>
<p>Quindi, non tutti i caratteri erano disponibili ovunque, e spesso le fonderie si copiavano tra loro.</p>
<p><strong>I font più usati al mondo sono stati tutti realizzati nel 1900</strong>, quando non c'erano neanche i PC.</p>
<p>La prima macchina da scrivere che permetteva di cambiare font fu la IBM Selectric, cambiando la testina. Ecco un video della Selectric all'opera:</p>
<p><blockquote class="imgur-embed-pub" lang="en" data-id="zCg1LX1"><a href="https://imgur.com/zCg1LX1">IBM Selectric ball head typewritter </a></blockquote><script async src="https://s.imgur.com/min/embed.js" charset="utf-8"></script></p>
<p>Torniamo al libro: Oltre ai font più famosi e a curiosità tipografiche, c'è anche la storia delle persone che lo hanno realizzato, <strong>gente che lavora per un ideale di bellezza molto diverso da quello che immaginiamo noi</strong>. Nessuna è diventata ricca.</p>
<p>Il font più usato al mondo risulta infatti l'<strong>Helvetica</strong>, specialmente per i titoli:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/2/28/HelveticaSpecimenCH.svg" alt="" /></p>
<p>Altri font famosissimi sono il <strong>Gill Sans</strong>, il <strong>Futura</strong>, l'<strong>Univers</strong>...</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/4/48/GillSansEG.svg" alt="" /></p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Futura_Specimen.svg/1200px-Futura_Specimen.svg.png" alt="" /></p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/8/8e/UniversSpec.png" alt="" /></p>
<h3>Chi dovrebbe leggere questo libro</h3>
<p>Avete mai sentito la leggenda metropolitana che basta cambiare la font di un sito e questo sembrerà completamente rinnovato? Che ci crediate o no, il mondo si accorge di queste cose. <a href="http://www.bloggokin.it/2009/09/15/ikea-font-verdana-o-futura/">Nel 2009 Ikea fu messa alla gogna per aver cambiato il font del suo catalogo, dal Futura al Verdana</a>...</p>
<p>Io che sono un programmatore, e che spesso mi trovo a implementare il design degli altri, ho trovato questo libro interessante e gradevole.</p>
<p>In generale <strong>se vi occupate di design a qualsiasi livello (packaging, pubblicità, marketing, web...), questo libro dovrebbe essere nella vostra lista</strong>.</p>
<p><iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-eu.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=ilblodimicnas-21&o=29&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=B00QFIVE8I&linkId=c330ea265a0ba9192f74f16adb252a00"></iframe></p>
Get the list of files blocked by a process in Linux
2018-06-18T00:00:00Z
https://michelenasti.com/2018/06/18/get-the-list-of-files-blocked-by-a-process-in-linux.html
<p>Problem: <strong>I had a process using 90% of cpu, couldn't understand what was going on.</strong></p>
<p>After seeking the log files and other stuff, I still couldn't undestand why it was taking so much CPU.</p>
<p>I decided to give a look at what files are used by this process.</p>
<p><img src="https://michelenasti.com/images/find-file-linux-code_magnifying_glass_zero-1" alt="" title="find files blocked by processes in linux" /></p>
<h2>It's so easy</h2>
<p>first, <strong>find the PID of your process.</strong> My preferred way to do it is with the command:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ps</span> <span class="token parameter variable">-ef</span> <span class="token operator">|</span> <span class="token function">grep</span> <span class="token operator"><</span>command_name<span class="token operator">></span></code></pre>
<p>you'll see an output like this:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ps</span> <span class="token parameter variable">-ef</span> <span class="token operator">|</span> <span class="token function">grep</span> <span class="token operator"><</span>command<span class="token operator">></span><br />root <span class="token number">13141</span> <span class="token number">1</span> <span class="token number">69</span> <span class="token number">10</span>:36 ? 02:52:34 /path/to/<span class="token operator"><</span>command<span class="token operator">></span><br />admin <span class="token number">31182</span> <span class="token number">30798</span> <span class="token number">0</span> <span class="token number">14</span>:45 pts/2 00:00:00 <span class="token function">grep</span> <span class="token parameter variable">--color</span><span class="token operator">=</span>auto <span class="token operator"><</span>command<span class="token operator">></span></code></pre>
<p>The pid of this process is <code>13141</code>.</p>
<p>And now <strong>let's get the list of files blocked by this process</strong>.</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ls</span> <span class="token parameter variable">-al</span> /proc/13141/fd </code></pre>
<p>The system might ask you to run this command as privileged user, so don't disregard using <code>sudo</code>.</p>
<p>output:</p>
<pre class="language-bash"><code class="language-bash">total <span class="token number">0</span><br />dr-x------ <span class="token number">2</span> root root <span class="token number">0</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token builtin class-name">.</span><br />dr-xr-xr-x <span class="token number">8</span> root root <span class="token number">0</span> Jun <span class="token number">19</span> <span class="token number">10</span>:36 <span class="token punctuation">..</span><br />lrwx------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">0</span> -<span class="token operator">></span> /dev/null<br />lrwx------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">1</span> -<span class="token operator">></span> /dev/null<br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">14</span>:30 <span class="token number">10</span> -<span class="token operator">></span> /run/sdk/user.uid.db<br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">14</span>:30 <span class="token number">11</span> -<span class="token operator">></span> /run/sdk/user.shadow.db<br />lrwx------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">2</span> -<span class="token operator">></span> /dev/null<br />lrwx------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">3</span> -<span class="token operator">></span> socket:<span class="token punctuation">[</span><span class="token number">2342197</span><span class="token punctuation">]</span><br />lrwx------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">4</span> -<span class="token operator">></span> socket:<span class="token punctuation">[</span><span class="token number">2342201</span><span class="token punctuation">]</span><br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">5</span> -<span class="token operator">></span> anon_inode:inotify<br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">6</span> -<span class="token operator">></span> /backup_rsync/backup/20170825-1225/lib/azure-cli/lib/python2.7/site-packages/azure/mgmt/network/v2017_03_01/models<br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">12</span>:12 <span class="token number">7</span> -<span class="token operator">></span> /run/sdk/lock/lock_pw<br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">14</span>:30 <span class="token number">8</span> -<span class="token operator">></span> /run/sdk/user.name.db<br />lr-x------ <span class="token number">1</span> root root <span class="token number">64</span> Jun <span class="token number">19</span> <span class="token number">14</span>:30 <span class="token number">9</span> -<span class="token operator">></span> /run/sdk/user.misc.db</code></pre>
<p>Apart from a lot of stuff that seems creepy (<code>/dev/null</code>, <code>socket:\\\[...\\\]</code>), I can clearly spot a file used by my process: the one starting with <code>/backup_rsync</code>.</p>
<p>Running this command other two-three times, I understood that it is not hung, it is just scanning every file in my system and checking against a pattern. Very poor design.</p>
<p>Hope this helps!</p>
Come creare luoghi di lavoro in cui é bello recarsi
2018-06-19T00:00:00Z
https://michelenasti.com/2018/06/19/come-creare-luoghi-di-lavoro-in-cui-le-persone-sono-felici-di-recarsi.html
<p>Dovreste saperlo un po' tutti, ormai, che <strong>lavoro da remoto</strong>. Ai tempi feci questa scelta perchè non ce la facevo più a viaggiare 2 ore della mia vita in un autobus che restava puntualmente imbottigliato nel traffico. Ma <strong>il lavoro "tradizionale" è così brutto?</strong></p>
<p>Estremizziamo: ha senso, nel 2018, parlare ancora di uffici? E' plausibile prevedere che <em>nel 2030 lavoreremo tutti da casa?</em></p>
<p><strong>Ho conosciuto molte persone che sono felici di andare a lavoro</strong>, che amano la compagnia dei colleghi, e che si trovano bene col proprio capo. Per queste persone i soldi non sono importanti (ma guadagnano quanto basta per vivere felicemente).</p>
<p><strong>Recarsi per 200 giorni all'anno in un posto in cui devi trascorrerci almeno 8 ore, e non trovarsi bene, è un suicidio morale.</strong></p>
<p>Quello che quindi consiglio al tuo boss è: <strong>rendi l'ufficio un posto in cui i tuoi dipendenti VOGLIONO andare!</strong> (...E non dove <em>devono</em> andare). Vediamo come.</p>
<p><img src="https://michelenasti.com/images/Indoor-Garden-For-Office-Building-2013-Glass-Ceiling-With-Steel-And-Wooden-Frame-Design.jpg" alt="" title="Figo un ufficio così, vero?" /></p>
<blockquote>
<p>Se le caffetterie hanno giocato un ruolo fondamentale nell’Illuminismo, quanto è importante lo spazio dove si lavora nel decretare il successo dei nostri progetti?</p>
</blockquote>
<p>E' probabile che nell'Illuminismo non avevano possibilità di sperimentare col lavoro da remoto, ma <strong>grandi compagnie che guidano il futuro</strong> (Google, Apple...) <strong>si sono apertamente schierate contro il remote working</strong> (e a favore dello smark working, e di questo ne parleremo in un altro articolo). <strong>L'obiettivo è di favorire la comunicazione e lo scambio di idee, e il lavoro da remoto sembra essere un ostacolo.</strong></p>
<blockquote>
<p>serve molto di più per creare un posto dove le idee migliori emergano, dove si possa essere produttivi e, soprattutto, a proprio agio.</p>
</blockquote>
<p><strong>Non basta una sede con biliardini e tavoli da ping pong</strong>: nelle sedi in cui ho visto queste cose, i dipendenti hanno candidamente ammesso che non li usano per non disturbare gli altri. Il cambio deve essere culturale!</p>
<blockquote>
<p>ci siano essenzialmente due motivi per cui lavorare sotto lo stesso tetto sia di valore: stringere legami e creare idee.</p>
</blockquote>
<p>L'uomo è animale sociale, e se sei un tipo solitario, <strong>i colleghi saranno le persone che vedrai di più (non può farti che bene)</strong>. I legami che si stringono con i colleghi possono durare più del rapporto di lavoro. E all'inizio della carriera i più esperti possono farti da mentore e aiutarti nelle scelte e a diventare più bravo.</p>
<p>Ma la cosa più interessante è il creare idee. Ci piace pensare che il <em>genio</em> esca all'improvviso, magari in sogno, o più semplicemente mentre si sta facendo altro e si viene folgorati da un'intuizione. Invece sembra che le idee nascano in modi profondamente diversi; <strong>le connessioni casuali con persone che hanno altre idee genera un mix di confusione e chiarezza che porta a ciò che chiamiamo creatività</strong>.</p>
<h2>Due parole sul caffè</h2>
<p><strong>Quando vuoi parlare con un collega, è molto probabile che tu gli dica "andiamoci a prendere un caffè".</strong></p>
<p><img src="https://michelenasti.com/images/caffe-distributore-automatico-ufficio_-672x351.jpg" alt="" /></p>
<p>E' stato dimostrato che le chiacchierate al distributore (o alla macchinetta del caffè) sono <a href="https://www.nytimes.com/2012/07/15/jobs/group-breaks-can-raise-workplace-productivity.html">fondamentali per aumentare la produttività</a>.</p>
<p><em>Come scusa? Ma alla macchinetta del caffè non si cazzeggia?</em></p>
<p>No(n solo), alla macchinetta del caffè si cazzeggia pure, ma più spesso si chiacchiera dei progetti in corso, si chiede aiuto per superare un ostacolo.</p>
<blockquote>
<p>se avete introdotto policy per ridurre durata e frequenza delle pause caffè in azienda, c’è il rischio che stiate mettendo i bastoni fra le ruote all’innovazione!</p>
</blockquote>
<h2>La ricetta per l'ambiente di lavoro perfetto</h2>
<p>Non ce l'ho, ma posso provare a raccontare i miei desiderata:</p>
<ul>
<li><strong>Niente regole su come e quando andare alla macchinetta del caffè.</strong></li>
<li><strong>Niente regole sulle cuffiette</strong>. I programmatori hanno bisogno di lunghe ore di concentrazione per entrare "in the zone".</li>
<li><strong>Organizza cene</strong>, uscite, viaggi, visite con l'ufficio. Coinvolgi le famiglie. (Ne bastano un paio all'anno!)</li>
<li>Se possibile, <strong>prevedi un parcheggio aziendale</strong>. I mezzi di trasporto non sono eccellenti (e puntuali) in tutte le città del mondo!</li>
<li><strong>Aiuta le persone a tirare fuori idee nuove</strong> sul prodotto, sull'ufficio, sull'organizzazione. <strong>Ascolta tutti</strong>.</li>
<li><strong>Investi sulla formazione</strong>. Non significa solo mandare la gente ai corsi. Significa comprare libri, ascoltare loro in che direzione vogliono andare con la loro specializzazione, e soprattutto <strong>dargli il tempo di studiare durante l'orario di lavoro</strong>. Su questo punto tornerò in un altro articolo perchè è determinante anche per l'innovazione interna.</li>
</ul>
<p><strong>I dipendenti sono l'asset che vale di più, non farlo svalutare!</strong></p>
<h2>Se ti piace questo argomento...</h2>
<p>Ti consiglio questo libro, <a href="https://amzn.to/2toNoae">Human centered work</a>, da cui ho preso le citazioni che hai letto in questo articolo.</p>
<p><iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-eu.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=ilblodimicnas-21&o=29&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=8823836425&linkId=c724d19e9ba8713adb49f1e18311177b"></iframe></p>
Designing the documentation for a tech product
2018-07-09T00:00:00Z
https://michelenasti.com/2018/07/09/designing-the-documentation-for-a-tech-product.html
<p>What makes a product a <em>great</em> product?</p>
<p>one might say the <strong>features</strong>, others may say the <strong>ease of using</strong> these features writing less code, with more throughput, etc.</p>
<p>I agree with this definition; everyday in my job I use tools that can deploy rockets to the moon and others that can at most change the color of a text. But let me tell you how I choose my lethal weapons: by their <strong>documentation</strong>.</p>
<p><img src="https://michelenasti.com/images/documentation.png" alt="" /></p>
<h2>One of the last things a developer thinks about</h2>
<p>you write a fantastic tool that automatizes a part of your job and you think it might be useful for others, too. What do you do?</p>
<ul>
<li>Put on github</li>
<li>Write a simple Readme.md</li>
<li>Wait for people coming</li>
</ul>
<p>And nobody comes. Or, people coming will just watch and pass by, preferring another older tool with less features.</p>
<p>To spread your product as much as possible you have to impersonate your users and think like them. In your Readme you have to write <em>at least</em>:</p>
<ul>
<li>What does this tool do</li>
<li>How</li>
<li>What do you need to start/install the tool</li>
<li>How to configure it</li>
<li>...</li>
</ul>
<p>This applies to commercial tools too, infact <strong>it is the last free thing you can do to spread your product</strong> - marketing costs money!</p>
<h2>My job for the last two years</h2>
<p>In my last two years I've worked (and still working) for a very big digital product. (I have worked also on other things, but this has been one of my exclusive tasks). I started with knowing nothing about it, and now I know it better than anyone else.</p>
<p>My job? Designing the documentation website. I had to explain to a complete ignorant, like myself, how to use this product to obtain any possible goal.</p>
<p>It was not an easy journey: for example, available documentation was written in <em>pdf</em> or <em>doc</em> documents, not the best solution in 2016. And not all the documentation was publicly accessible.</p>
<p>The product is old: it started in 1999 and still kicks asses!</p>
<p>The product is big: competitors don't have many features this product has.</p>
<p>So: what's the job?</p>
<h2>My solution: two websites for one documentation</h2>
<p>I've decided to create two websites:</p>
<ul>
<li>The first one explains, in current English, every possible feature in the form of a tutorial. Many topics are explored in sections (for example there's the <em>security</em> section).</li>
<li>The second website contains just the APIs and explains, for every API, what every field does. It also contains examples of requests and responses.</li>
</ul>
<p>The first website was made using <a href="https://jekyllrb.com/">jekyll</a>; the second was created using a tool called <a href="https://github.com/lord/slate">Slate</a>.</p>
<p>In the first website I think I have covered everything: form how to sign up to how to handle custom configurations.</p>
<p>Every API referenced in the jekyll documentation is linked to the slate one, so a user can easily dive in details.</p>
<h2>The most difficult part</h2>
<p>Architecturing the whole solution was the toughest job.</p>
<p>I had to be sure everything was clear enough for newcomers, and also clear for experienced customers.</p>
<p>I had to organize stuff into chapters, sections, remembering where and when I was going to talk about a topic and to link it in the future.</p>
<p>Then, the job of actually writing the documentation was easy (and a little bit boring!) because the most difficult part was already done.</p>
<p>I've not mentioned other technical aspects: allow users to search the docs, deploy a beta version of the docs for internal and review purposes, handle versions...</p>
<h2>Feedbacks</h2>
<p>During my job I have talked to many people who has read "my" documentation. They told me it was extremely clear and they loved the examples, it made the whole thing more readable.</p>
<p>Even my bosses think that the documentation is helping them in expanding the product to new potential customers.</p>
<p>I feel proud that my job is helping the company and customers to reach their respective goals. I really enjoy working for public products!</p>
Handling configuration for multiple environments in NodeJS
2018-07-22T00:00:00Z
https://michelenasti.com/2018/07/22/laravel-style-config-in-nodejs.html
<p>Imagine you have a NodeJS app you're writing, and this app runs on several different environments:</p>
<ul>
<li>on your <strong>developer</strong> PC, it should use some environment variables (e.g. database connection to <em>localhost</em>, port to use <em>3000</em>...)</li>
<li>when you push your code you may want to run some continuous integration & deployiment on a <strong>test environment</strong>, so you have to configure new environment variables: database now points to <em>192.168.xx.yy</em>, port is <em>8000...</em></li>
<li>Finally, on the <strong>production environment</strong> you use the official values for these configuration.</li>
</ul>
<p>How do you handle this? There are several ways to accomplish this task, I wanted to replicate the simple, easy solution provided by <em>Laravel</em> in <strong>NodeJS</strong>.</p>
<h2>How does laravel config works</h2>
<p>In <a href="https://laravel.com/docs/5.6/configuration">Laravel</a> you create as many env files you want, for example:</p>
<ul>
<li>
<p><code>.dev.env</code>, that contains configuration for development environment</p>
</li>
<li>
<p><code>.test.env</code>, for test environment</p>
</li>
<li>
<p><code>.prod.env</code> for production</p>
</li>
<li>
<p>Then there's a last file called <code>.env</code> that contains a single variable:</p>
<p><code>APP_ENV=dev</code> (or test, or prod).</p>
</li>
</ul>
<p>We're in NodeJS, how do we simulate this behaviour?</p>
<h2>a Node solution</h2>
<p>First, our configuration files are js files:</p>
<ul>
<li><code>.development.js</code></li>
<li><code>.test.js</code></li>
<li><code>.production.js</code></li>
</ul>
<p>Naming of files has changed a little bit, we'll see why in a while.</p>
<p>Let's see the content of the .development.js test file:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">//.development.js </span><br /><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token constant">API_URL</span><span class="token operator">:</span> <span class="token string">'api_url'</span><span class="token punctuation">,</span><br /> <span class="token constant">API_KEY</span><span class="token operator">:</span> <span class="token string">'api_key'</span><span class="token punctuation">,</span><br /> <span class="token constant">SECRET_KEY</span><span class="token operator">:</span> <span class="token string">'secret_key'</span><br /><span class="token punctuation">}</span></code></pre>
<p><code>.production.js</code> and <code>.test.js</code> will contain an object with the same keys but different values.</p>
<h2>The environment variable NODE_ENV</h2>
<p>Since Express became the most popular application server in Node, the NODE_ENV variable has became popular too. In Express, <a href="https://stackoverflow.com/a/16979503/1020090">app.get('env') is used to retrieve the execution environment</a>. Many popular platforms, like Heroku, set this variable to <code>production</code> and you can also change to your needs, too.</p>
<p><code>app.get('env')</code> is pretty much implemented like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">return</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">||</span> <span class="token string">'development'</span></code></pre>
<p>So you know that if the variable is not set, it is defaulted to <code>development</code>.</p>
<p>Let's write a solution that is not express-dependent.</p>
<p>Here's the configuration.js file:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//configuration.js </span><br /><br /><span class="token keyword">const</span> env <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">||</span> <span class="token string">'development'</span><br /><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">../.</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>env<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.js</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span> </code></pre>
<p>Based on the <code>NODE_ENV</code> variable, we will pick up the right <code>.js</code> file.</p>
<p>And now let's test this. Run <code>node index.js</code> (should start with <code>development</code> environment):</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//index.js</span><br /><br /><span class="token keyword">const</span> configuration <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./configuration'</span><span class="token punctuation">)</span><br /><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'configuration: '</span><span class="token punctuation">,</span> configuration<span class="token punctuation">)</span><br /><span class="token comment">// => </span><br /><span class="token comment">// configuration: { API_URL: 'api_url',</span><br /><span class="token comment">// API_KEY: 'api_key',</span><br /><span class="token comment">// SECRET_KEY: 'secret_key' }</span></code></pre>
<p>How to start with other configurations?</p>
<p>Either set this in package.json:</p>
<pre class="language-json"><code class="language-json"> <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"node index.js"</span><span class="token punctuation">,</span><br /> <span class="token property">"start:test"</span><span class="token operator">:</span> <span class="token string">"NODE_ENV=test node index.js"</span><span class="token punctuation">,</span><br /> <span class="token property">"start:prod"</span><span class="token operator">:</span> <span class="token string">"NODE_ENV=production node index.js"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<p>and run with <code>npm run start:test</code>; or set the <code>NODE_ENV</code> variable directly on the server (docker, heroku, aws... they all allow you to set env variables).</p>
<h2>Some security concerns</h2>
<p>This approach is very easy to use but you should not versionate <code>.production.js</code> file, as it will contain sensitive data. In your build process consider injecting this file from some other secure location.</p>
<p>An alternative could be to use environment variables only for the production file:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">//.production.js </span><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token constant">API_URL</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">API_URL</span> <span class="token operator">||</span> <span class="token string">'some_value'</span> <span class="token punctuation">,</span><br /> <span class="token constant">API_KEY</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">API_KEY</span><span class="token punctuation">,</span><br /> <span class="token constant">SECRET_KEY</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">SECRET_KEY</span><span class="token punctuation">,</span><br /> <span class="token operator">...</span> <br /><span class="token punctuation">}</span></code></pre>
<p>The good thing about this approach is that you can even mix the two things or swap them altogether.</p>
<p>Happy coding with your configurations ;)</p>
"ma... cosa intendete per testing?" - una breve introduzione al testing software
2018-08-21T00:00:00Z
https://michelenasti.com/2018/08/21/ma-cosa-intendete-per-testing-una-breve-introduzione-al-testing-software.html
<p>Mi chiedeva Emanuele su Quora:</p>
<blockquote>
<p>...posso sapere con una certa precisione cosa intendete con testing? Questa cosa mi ha incuriosito e mi piacerebbe conoscerne di più.</p>
</blockquote>
<p><img src="https://michelenasti.com/images/Testing_in_Progress.gif" alt="" /></p>
<p>Per testing si intende <strong>un’esecuzione del codice per assicurarsi che faccia esattamente ciò che viene richiesto</strong>.</p>
<p>Esistono sostanzialmente due tipi di testing, <em>manuale</em> e <em>automatico</em>. Il <strong>testing manuale</strong> è quello che fa un programmatore dopo che ha sviluppato il codice: lancia l’applicazione e verifica che faccia ciò che dovrebbe. A volte sono altri utenti in carne ed ossa a fare il test dell’applicazione.</p>
<p>Il testing manuale è efficace e immediato se l’app è semplice o di piccole dimensioni: al crescere della complessità potrebbe essere lungo in termini di tempo, e impreciso nei risultati.</p>
<p>Esempio: <em>un’app per prenotare visite mediche</em>. Immaginiamo di aver realizzato la funzionalità di modifica della data di una visita. Se faccio l’edit di una visita e seleziono una data e un orario che già sono state prenotate da qualcun altro, il sistema deve rispondere che l’orario richiesto non è disponibile. Riesci a immaginare quanto tempo serva per poter rieseguire questo caso di test, ogni volta? <em>(Ti aiuto: bisogna prima creare una visita... poi crearne una seconda... poi prendere la prima e impostare la data e l'orario della seconda... e verificare il messaggio di errore)</em></p>
<p>Passiamo al <strong>test di tipo automatico</strong>. Innanzitutto viene scritto in un linguaggio di programmazione vero e proprio. Nel test viene definito il setup iniziale (es. crea DUE visite nel sistema) e poi viene eseguito il test vero e proprio, ossia cerca la prima visita, e cambia la data e l’orario a quello della seconda.</p>
<p>Infine, controlliamo che il sistema abbia restituito l’errore. Se è accaduto, il test ha avuto successo (si, se il sistema risponde correttamente che un’operazione non si può fare, è un caso di successo!). Se così non fosse, il test dovrebbe fallire.</p>
<p><strong>Quali sono gli svantaggi di questo approccio?</strong></p>
<ul>
<li>che il test lo può scrivere solo un programmatore,</li>
<li>per scrivere il test ci vuole del tempo un po’ più lungo della mera esecuzione;</li>
<li>inoltre, se cambia l’applicazione, bisogna cambiare anche i test relativi.</li>
</ul>
<p><strong>I vantaggi, invece, sono un aumento della qualità generale e un’efficienza nell’esecuzione</strong>, anche in termini di tempo. Ad esempio, è prassi eseguire tutti i test ad ogni modifica del sistema, per assicurarsi di non aver rotto aree dell’applicazione collegate con quella che stiamo modificando e che difficilmente andremmo a testare “manualmente”.</p>
<p>In questa risposta ho dato una visione molto generica del software testing; esistono tante altre sotto-aree (integration, unit, e2e…) e tecniche da conoscere, ma il senso generale è che si, <strong>un computer può fare dei test al posto tuo, è molto più veloce di te, e ne guadagni in qualità.</strong></p>
Things you may not know about Object Oriented Javascript (Es6)
2018-09-02T00:00:00Z
https://michelenasti.com/2018/09/02/some-things-you-may-not-know-about-object-oriented-javascript-es6.html
<p>Hi there! Let's try to create a simple <code>Group</code> object, very similar to the <a href="https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Set">existing <code>Set</code> object</a> in Javascript. Here's the specification for this Abstract Data Type:</p>
<ol>
<li>It should provide a constructor that creates an empty Group.</li>
<li>An object can be contained only once; there cannot be duplicates.</li>
<li>It must provide <code>add()</code>, <code>delete()</code> and <code>has()</code> methods.</li>
<li>There must be a <code>length</code> property containing the number of objects stored inside.</li>
<li>Provide a static method that creates a <code>Group</code> from an array.</li>
</ol>
<blockquote>
<p>We'll use this example to explain stuff regarding javascript new <code>class</code> keyword introduced in ES6. We will <strong>not</strong> focus on the performance of this class; only on js syntax.</p>
</blockquote>
<h2>The constructor</h2>
<p>Let's start from the basics. Here's the constructor for this object:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Group</span> <span class="token punctuation">{</span><br /> <br /> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>elements <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span></code></pre>
<p>The <code>Group</code> class has an internal array that will hold the list of objects.</p>
<h2>The has() method</h2>
<p>Let's start with the <code>has</code> method, because it will be used widely in other methods. This method should check that an element is already in the group. Here's the code:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Group</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token function">has</span><span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>elements<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span> <span class="token operator">>=</span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token boolean">true</span> <span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Basically we leave to <code>indexOf()</code> the burden of checking if the element is already present in our group.</p>
<h2>add() and delete() method</h2>
<p>If an element is not present, <code>add</code> should add the element to the group. And of course, if an element is present, <code>delete</code> should... delete.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Group</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>elements<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">delete</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>elements<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>elements<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <br /> <span class="token punctuation">}</span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span></code></pre>
<p>What's going on in the <code>delete()</code> method? We search for the index of the element in the array, then we call the <code>[splice()](https://www.w3schools.com/jsref/jsref_splice.asp)</code><a href="https://www.w3schools.com/jsref/jsref_splice.asp"> function</a> that returns a new array without the element at the specified index. The second argument is the number of elements to remove.</p>
<h2>"That don't impress me much". Fun begins now: getters and setters</h2>
<p>Let's see how to create a <em>derived</em> property in a javascript object. A derived property is a property that changes when another property changes. For example we want to write a derived property to get the number of the elements for the group.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Group</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token keyword">get</span> <span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>elements<span class="token punctuation">.</span>length<br /> <span class="token punctuation">}</span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span></code></pre>
<ol>
<li>there's the keyword <code>get</code> that specifies this property as a <em>getter</em>. When we call <code>group.length</code> (<strong>note: no parenthesis!</strong>) we get the length of the inner array. We can write very complex stuff in here, and we can access the <code>length</code> property as a normal property - the user will not know what's going on internally.</li>
<li>There's also the <code>set</code> keyword that you can use for setting a property. Here's an example:</li>
</ol>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Example</span> <span class="token punctuation">{</span><br /> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token number">3</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">set</span> <span class="token function">property</span><span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>hidden <span class="token operator">=</span> element<br /> <span class="token punctuation">}</span><br /> <br /> <span class="token keyword">get</span> <span class="token function">property</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>hidden<span class="token operator">*</span><span class="token keyword">this</span><span class="token punctuation">.</span>hidden <br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">const</span> example <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Example</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>example<span class="token punctuation">.</span>hidden<span class="token punctuation">)</span><br /><span class="token comment">// --> 3</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>example<span class="token punctuation">.</span>property<span class="token punctuation">)</span><br /><span class="token comment">// --> 9, the getter returns the square of hidden</span><br />example<span class="token punctuation">.</span>property <span class="token operator">=</span> <span class="token number">4</span><br /><span class="token comment">// now `hidden` is set to 4</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>example<span class="token punctuation">.</span>hidden<span class="token punctuation">)</span><br /><span class="token comment">// --> 4</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>example<span class="token punctuation">.</span>property<span class="token punctuation">)</span><br /><span class="token comment">// --> 16</span></code></pre>
<p>This mechanism is very powerful and you can do a lot of stuff with it. VueJS uses this mechanism to create the Vue object filled with the <code>data</code> properties.</p>
<h2>Static methods</h2>
<p>Our last piece of exercise is to build a static method for the <code>Group</code> class.</p>
<p>A static method are called without instantiating their class and are callable when the class is not instantiated. Let's see an example:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Group</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token keyword">static</span> <span class="token function">from</span><span class="token punctuation">(</span>elements<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">//here we instantiate the object that will be returned</span><br /> <span class="token keyword">const</span> group <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Group</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token comment">//for semplicity let's assume that `elements` is an array. </span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> elem <span class="token keyword">of</span> elements<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> group<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>elem<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">return</span> group<br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">const</span> group <span class="token operator">=</span> Group<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"one"</span><span class="token punctuation">,</span> <span class="token string">"two"</span><span class="token punctuation">,</span> <span class="token string">"three"</span><span class="token punctuation">,</span> <span class="token string">"one"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token comment">// -> returns a Group </span><br /><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>group<span class="token punctuation">.</span>length<span class="token punctuation">)</span><br /><span class="token comment">// -> 3</span></code></pre>
<h2>Finally...</h2>
<p>I don't suggest you to program in JS as you would in Java; JS object oriented features in JS are very different compared to other languages. However, if you find that something should be an object, and you end up writing an Object, there two tricks might become handy.</p>
<p>Next time we'll talk about iterators and symbols in Javascript, another nice addition to the language. Stay tuned!</p>
Symbols & Iterators in Javascript
2018-09-03T00:00:00Z
https://michelenasti.com/2018/09/03/symbols-iterators-in-javascript.html
<p>ES6 introduced the new <code>for ... of</code> syntax to iterate a collection. Did you know? And how does it work?</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'banana'</span><span class="token punctuation">,</span> <span class="token string">'apple'</span><span class="token punctuation">,</span> <span class="token string">'orange'</span><span class="token punctuation">]</span><br /><br /><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> fruit <span class="token keyword">of</span> arr<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>fruit<span class="token punctuation">)</span><br /><span class="token punctuation">}</span><br /><span class="token comment">// -> banana</span><br /><span class="token comment">// -> apple</span><br /><span class="token comment">// -> orange</span></code></pre>
<h3>How does it work</h3>
<p>What you are seeing is a syntax that iterates on an <strong>iterator</strong>, a common concept in other languages but fairly new in Javascript.</p>
<p>Basically, if an object has an <em>iterator</em> you can use the <code>for...of</code> syntax to iterate on it.</p>
<h3>How do we create an iterator?</h3>
<p>To create iterators, we must provide a new property in our object called <code>Symbol.iterator</code>. <em>...what ?</em></p>
<h2>What is a Symbol</h2>
<p>a Symbol is a javascript function that returns a value that is guaranteed to be unique.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> helloSymbol <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token string">'hello'</span><span class="token punctuation">)</span></code></pre>
<p>They accept names, to be easily recognized and debugged, but if you create two symbols with the same name, they are not equal:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> helloSymbol2 <span class="token operator">=</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token string">'hello'</span><span class="token punctuation">)</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>helloSymbol <span class="token operator">===</span> helloSymbol2<span class="token punctuation">)</span><br /><span class="token comment">/// -> false </span></code></pre>
<h3>Why do we <em>need</em> symbols?</h3>
<p>First, I've seen a similar concept in other languages, like Ruby. They are useful to define properties (or functions) that are shared across different types of objects. You could use symbols to uniquely identify properties in your maps, for example.</p>
<p>There is one symbol that has been already created by the Javascript runtime, and it's called <code>Symbol.iterator</code>. **If an object has a property named <code>Symbol.iterator</code>, it is iterable.</p>
<h2>How do we create an Iterator for our objects</h2>
<p>To create an iterator the first thing to do is to attach the property:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Example</span> <span class="token punctuation">{</span><br /> <span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>As you can see, this is a legitimate name for a property. Infact, <strong>allowed names for properties are strings and symbols</strong>.</p>
<p>The only caveat is that we have to use the square brackets to define the property name, since it is an object.</p>
<p>The iterator must return a function that returns an object, containing a <code>next()</code> function.</p>
<p>This <code>next()</code> function, in turn, returns an object with two properties, <code>value</code> (the actual element of the iteration) and <code>done</code> (set to <code>true</code> when the iteration has ended).</p>
<p>Confused?</p>
<h3>Example</h3>
<p>Do you remember the <code>Group</code> class we <a href="https://michelenasti.com/2018/09/03/some-things-you-may-not-know-about-object-oriented-javascript-es6.html">designed in the last article</a>? Basically it is a Set object with another name. It holds just one copy for every object (duplicates are not allowed) and we use a simple array to store data inside. Let's write an iterator for it!</p>
<pre class="language-javascript"><code class="language-javascript"><br /><span class="token keyword">const</span> Group <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// a reference of internal elements...</span><br /> <span class="token keyword">const</span> elements <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>elements<span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>elements<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <br /> <span class="token comment">//the index we'll use to track the next element to return</span><br /> <span class="token keyword">let</span> i<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">//we return a next() function that will be called many times, </span><br /> <span class="token comment">//once for every element in the Group </span><br /> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> i<span class="token operator">++</span><span class="token punctuation">;</span><br /> <span class="token comment">//the next function returns an object with two properties</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">//in value we store the actual element we're returning </span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> elements<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token comment">// if true, the iteration stops </span><br /> <span class="token literal-property property">done</span><span class="token operator">:</span> i <span class="token operator">===</span> elements<span class="token punctuation">.</span>length <span class="token operator">?</span> <span class="token boolean">true</span> <span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//Some tests! </span><br /><span class="token comment">// You find the code for the Group class in the previous article ;) </span><br /><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> value <span class="token keyword">of</span> Group<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"a"</span><span class="token punctuation">,</span> <span class="token string">"b"</span><span class="token punctuation">,</span> <span class="token string">"c"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token comment">// → a</span><br /><span class="token comment">// → b</span><br /><span class="token comment">// → c</span></code></pre>
<p>....And this is how we create iterable objects in Javascript.</p>
<p>Easy, isn't it?</p>
<p>Just to let you know, (don't know if it's useful..) <code>String</code> is iterable, so you can write this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> hello <span class="token operator">=</span> <span class="token string">"Hello"</span> <br /><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> letter <span class="token keyword">of</span> hello<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>letter<span class="token punctuation">)</span><br /><span class="token punctuation">}</span><br /><span class="token comment">// -> H</span><br /><span class="token comment">// -> e</span><br /><span class="token comment">// -> l</span><br /><span class="token comment">// -> l</span><br /><span class="token comment">// -> o</span></code></pre>
<p>See you in the next iteration!</p>
Javascript: call functions without using parentheses (what?!)
2018-09-19T00:00:00Z
https://michelenasti.com/2018/09/19/javascript-chiamare-funzioni-senza-usare-parentesi-(what!).html
<p>Let's dig in a not-well-publicized ES6 feature: calling functions without using parentheses.</p>
<p>If you are familiar with Ruby, you know that in Ruby you can omit parentheses when they're not ambiguous:</p>
<pre class="language-ruby"><code class="language-ruby">puts <span class="token string-literal"><span class="token string">'hello world'</span></span> <br />puts<span class="token punctuation">(</span><span class="token string-literal"><span class="token string">'hello world'</span></span><span class="token punctuation">)</span> <br /><span class="token operator">/</span><span class="token operator">/</span><span class="token operator">-</span><span class="token operator">></span> same result<span class="token operator">!</span></code></pre>
<p>But we're in javascript and this is not allowed. Uhm... <strong>in some forms it <em>IS</em> allowed!</strong></p>
<h2>How I've discovered this: SQORN</h2>
<p>In my search for new libraries, I found <a href="https://sqorn.org/">SQORN</a> library. Sqorn allows you to write sql queries in nodejs.</p>
<p>What captured my attention is <em>the way Sqorn is intended to be used</em>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> sq <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'sqorn-pg'</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token keyword">const</span> kid <span class="token operator">=</span> sq<span class="token punctuation">.</span>from<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">person</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">.</span>where<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">age < 13</span><span class="token template-punctuation string">`</span></span> </code></pre>
<p>What's happening here?! Where are parentheses? <strong>Is this javascript after all?</strong></p>
<h2>Template strings</h2>
<p>You should already know the newest way of declaring a string in JS, like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> str <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">this is a string!</span><span class="token template-punctuation string">`</span></span></code></pre>
<p>And it is very useful because you can interpolate values inside, witouth concatenating:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'Michele'</span><span class="token punctuation">;</span><br /><span class="token comment">//es5</span><br /><span class="token keyword">var</span> helloES5 <span class="token operator">=</span> <span class="token string">"Hello, "</span> <span class="token operator">+</span> name<span class="token punctuation">;</span><br /><span class="token comment">//es6 </span><br /><span class="token keyword">const</span> helloES6 <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span></code></pre>
<p>Imagine you have to concatenate 6-7 items in the same sentence... You'll agree the ES6 version is clearer ;)</p>
<p><strong>The nice part of this string declaration is that you can pass strings as arguments to functions without parentheses:</strong></p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span> <span class="token function">hello</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">How are you </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// The convention is to write the string right </span><br /><span class="token comment">// after the function name...</span><br />hello<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Michele</span><span class="token template-punctuation string">`</span></span> <br /><span class="token comment">//-> How are you Michele </span><br /><br /><span class="token comment">//...but you can put a space too </span><br />hello <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Michele </span><span class="token template-punctuation string">`</span></span><br /><span class="token comment">//-> How are you Michele</span></code></pre>
<p>This syntax doesn't work with <code>'</code> or <code>"</code>:</p>
<pre class="language-javascript"><code class="language-javascript">hello <span class="token string">'Michele'</span><br /><span class="token comment">//-> SyntaxError: unexpected token: string literal</span></code></pre>
<h2>More power to string templates!</h2>
<p>Studying this syntax I discovered intresting features. For example, functions can extract the variables (ones in <code>${...}</code>) from the template string:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">function</span> <span class="token function">tag</span><span class="token punctuation">(</span><span class="token parameter">strings<span class="token punctuation">,</span> <span class="token operator">...</span>values</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>strings<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "Hello "</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>strings<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// " World "</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>values<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 15</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>values<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 50</span><br /> <br /> <span class="token keyword">return</span> <span class="token string">"Bazinga!"</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br />tag<span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> a <span class="token operator">+</span> b <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> World </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> a <span class="token operator">*</span> b <span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br /><span class="token comment">// "Bazinga!"</span></code></pre>
<h2>Conclusion</h2>
<p>This stuff is pretty nice but it is a bit obscure. Infact, apart from SQORN, I've never seen this syntax used elsewhere. <strong>It's a nice-to-know feature, good for impressing others, but my suggestion is to use this only if it's the clearest way to express your concepts.</strong></p>
Let's write our simple version of the require() function
2018-10-02T00:00:00Z
https://michelenasti.com/2018/10/02/let-s-write-a-simple-version-of-the-require-function.html
<p>You should know that Javascript (better to say EcmaScript) does not specify any function to read and write files.</p>
<p>In fact, <strong>Javascript is just the language used by many environments</strong> (the browser, or NodeJS, are examples of environments) that offer more objects and functions to work with.</p>
<p>Node was the first environment to offer a way to organize code in modules by using a special function called <code>require()</code>. How does it work? Let's try to implement it from zero.</p>
<p>Here is an example of <code>require</code> at work:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//test.js</span><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token string">"Hello World"</span><span class="token punctuation">;</span></code></pre>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//main.js</span><br /><span class="token keyword">const</span> test <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./test.js"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>test<span class="token punctuation">)</span> </code></pre>
<p>Let's write that <code>require</code> function.</p>
<h2>What should a require() function do</h2>
<p>a <code>require</code> function is expected to:</p>
<ul>
<li>read the content of a javascript file in a string</li>
<li>evaluate that code</li>
<li>save the exported function/object in a cache for later use (only read files once)</li>
</ul>
<h3>Disclaimer</h3>
<p>We will not rebuild the whole NodeJS in a single post. In fact, I will not implement many NodeJS checks and giggles, we are only interested in understand how things work.</p>
<p>We will still need the real <code>require</code> function to load the <code>fs</code> module. I'm not cheating, it's just that this post has to end sooner or later :)</p>
<h3>myRequire() function</h3>
<p>here's the code:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//file setup.js</span><br /><br /><span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />myRequire<span class="token punctuation">.</span>cache <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//(1)</span><br /><br /><span class="token keyword">function</span> <span class="token function">myRequire</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>name <span class="token keyword">in</span> myRequire<span class="token punctuation">.</span>cache<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <br /> <span class="token keyword">let</span> code <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readFileSync</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> <span class="token string">'utf8'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//(2)</span><br /> <span class="token keyword">let</span> module <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token literal-property property">exports</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">//(3)</span><br /> myRequire<span class="token punctuation">.</span>cache<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> module<span class="token punctuation">;</span> <span class="token comment">//(4) </span><br /> <span class="token keyword">let</span> wrapper <span class="token operator">=</span> <span class="token function">Function</span><span class="token punctuation">(</span><span class="token string">"require, exports, module"</span><span class="token punctuation">,</span> code<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//(5)</span><br /> <span class="token function">wrapper</span><span class="token punctuation">(</span>myRequire<span class="token punctuation">,</span> module<span class="token punctuation">.</span>exports<span class="token punctuation">,</span> module<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//(6)</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">return</span> myRequire<span class="token punctuation">.</span>cache<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">.</span>exports<span class="token punctuation">;</span> <span class="token comment">//(7)</span><br /><span class="token punctuation">}</span><br /><br /><span class="token operator">...</span></code></pre>
<h3>Did you forget to declare myRequire variable?</h3>
<p>No. In Javascript, functions declared with <code>function</code> keyword are evaluated before any other code (functions are "hoisted") so they can be referenced even before they're declared.</p>
<p>Also, functions can have properties (<em>this is javascript</em>!) so you can add the <code>cache</code> property to the <code>myRequire</code> function <strong>(step 1)</strong>.</p>
<p>Finally we're creating the <code>cache</code> property with <code>Object.create</code>. With this function we can specify the object prototype, we have chosen to not specify a prototype. Why? This way we don't mess with other functions or properties declared by the runtime. <a href="https://www.reddit.com/r/javascript/comments/5e62us/is_there_a_reason_to_create_an_object_without_a/">Here's an explanation</a>.</p>
<hr />
<p>Let's go back to <code>myRequire</code> . If the file we're importing is not in cache, we read the file from disk <strong>(step 2)</strong>.</p>
<p>Then we declare an empty <code>module</code> object with just one property, <code>exports</code> <strong>(step 3)</strong>.</p>
<p>We add this empty module to the cache, using the filename as the key, and then the magic happens <strong>(step 4)</strong>.</p>
<h2>The Function constructor</h2>
<p>In JS we can evaluate a string js code in two ways. The first way is via <code>eval()</code> function, that is a bit dangerous (it messes up the scope) so it is highly discouraged to use it.</p>
<p>The second way to evaluate code that we have in a string is via the <code>Function</code> constructor. This constructor takes a string with the arguments and a string with the code. This way everything has its own scope and doesn't mess things up for others.</p>
<p>So, basically we are creating a new function with these variables <strong>(step 5)</strong>: <code>require</code>, <code>exports</code>, and <code>module</code>. Let's think for a moment at the first example of this post, the file <code>test.js</code>: it becomes</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">require<span class="token punctuation">,</span> exports<span class="token punctuation">,</span> module</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token string">"Hello World"</span> <br /><span class="token punctuation">}</span></code></pre>
<p>and the second file, <code>main.js</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">require<span class="token punctuation">,</span> exports<span class="token punctuation">,</span> module</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> test <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./test.js"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>test<span class="token punctuation">)</span> <br /><span class="token punctuation">}</span></code></pre>
<p>Variables that seemed "global" in files are indeed passed as function arguments.</p>
<h2>Last step: executing the function</h2>
<p>We have created <strong>(step 6)</strong> a <code>wrapper</code> variable that holds a function, but the function is never executed. We do this at the line:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">wrapper</span><span class="token punctuation">(</span>myRequire<span class="token punctuation">,</span> module<span class="token punctuation">.</span>exports<span class="token punctuation">,</span> module<span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre>
<p>Note that the second variable (that should be <code>exports</code>) is just a handle to <code>module.exports</code>; NodeJS creators thought that this <a href="https://blog.tableflip.io/the-difference-between-module-exports-and-exports/">could have helped in writing less code...</a></p>
<p>When Node executes the function, everything that was "exported" (your public API) gets linked to the cache.</p>
<p>(Remember the <code>myRequire.cache[name] = module;</code> line? When it was first found by the compiler it was point to a dummy <code>{ exports: {} }</code> object; now it contains your module.)</p>
<blockquote>
<p>NOTE. Since we pass <code>myRequire</code> to the wrapper function, we can from now on use <code>require</code> in our test files, but our require gets called. Add a console.log if you don't trust me ;)</p>
</blockquote>
<p>Finally... <code>myRequire</code> returns the <code>export</code>ed stuff you declared <strong>(step 7)</strong>, and that we saved to the cache so we won't have to reevaluate this code again.</p>
<h2>Final considerations</h2>
<p>An example of this code <a href="https://github.com/musikele/require-example">can be found here</a>, along with some console logs that explain what's going on.</p>
<p>The idea of this article comes from the <a href="https://eloquentjavascript.net/10_modules.html#h_N33QHgUxbG">explanation of this function at chapter 10 (Modules)</a>. The book (<a href="https://www.eloquentjavascript.net/">Eloquent Javascript</a>) is excellent, but I had the urge to better understand, and try with a debugger, what I could not understand with my mind alone.</p>
<p>You should definitely read the book if you want to better understand javascript.</p>
Dovrei imparare Ruby on Rails o NodeJS ?
2018-10-15T00:00:00Z
https://michelenasti.com/2018/10/15/dovrei-imparare-ruby-on-rails-o-nodejs.html
<p>A questo link su Quora c'è la mia risposta alla domanda: <a href="https://it.quora.com/Dovrei-imparare-Ruby-on-Rails-o-Node-js/answer/Michele-Nasti">Dovrei imparare Ruby on Rails o NodeJS?</a></p>
Docker cheatsheet
2018-10-25T00:00:00Z
https://michelenasti.com/2018/10/25/docker-cheatsheet.html
<p>I usually forget everything if I don't use it that much. Here is a brief list of docker commands learned on the go.</p>
<h2>Start a simple container</h2>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-i</span> <span class="token parameter variable">-t</span> ubuntu /bin/bash</code></pre>
<ul>
<li><code>run</code> runs the <code>ubuntu</code> image</li>
<li><code>-i</code> allows you to write to the container ("Keep STDIN open even if not attached")</li>
<li><code>-t</code> allocates a pseudoTTY and allows to read from the container</li>
<li><code>ubuntu</code> is the name of the image to start</li>
<li><code>/bin/bash</code> is the command to launch once the image is started</li>
</ul>
<p>In practice: use <code>-i -t</code> to read and write from the docker image in the console.</p>
<p>Optional commands:</p>
<ul>
<li><code>--name XXX</code> assigns the name XXX to the container</li>
<li><code>-d</code> launches the container as a daemon</li>
<li><code>-c XXX</code> passes XXX to the command. In our example the command is <code>/bin/bash</code>. With -c we can pass any kind of commands; for example <code>-c "while true; do echo hello world; sleep 1; done"</code>. Docker would pass this string t bash and the container would print hello world every second without stopping. Note the quotes.</li>
</ul>
<h2>See containers status</h2>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> <span class="token function">ps</span> <span class="token parameter variable">-a</span> </code></pre>
<ul>
<li><code>ps</code> alone prints only the active containers</li>
<li><code>-a</code> shows also the exited containers</li>
</ul>
<h2>Inspecting a container</h2>
<p>If the container is started as daemon you might want to see the output.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> logs <span class="token parameter variable">-t</span> <span class="token parameter variable">-f</span> NAME_OF_CONTAINER </code></pre>
<ul>
<li><code>logs NAME_OF_CONTAINER</code> shows you the logs of the container. Without options you see the latest logs and then the propt gets back to your shell.</li>
<li><code>-f</code> works like <code>-f</code> in <code>tail -f</code>; logs become live. Use <code>CTRL-C</code> to exit.</li>
<li><code>-t</code> shows also timestamps.</li>
</ul>
<p>To check container processes:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">docker</span> <span class="token function">top</span> daemon_dave</code></pre>
<p>To get stats about a bunch of docker containers:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">docker</span> stats daemon_dave daemon_kate</code></pre>
<h2>Running programs in a container</h2>
<p>Daemon mode:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">sudo</span> <span class="token function">docker</span> <span class="token builtin class-name">exec</span> <span class="token parameter variable">-d</span> daemon_dave <span class="token function">touch</span> /etc/new_config_file</code></pre>
<ul>
<li><code>-d</code> stands for daemon mode.</li>
<li><code>exec</code> is used to run a command in the <code>daemon-dave</code> container.</li>
</ul>
<p>Interactive mode:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">docker</span> <span class="token builtin class-name">exec</span> <span class="token parameter variable">-t</span> <span class="token parameter variable">-i</span> daemon_dave /bin/bash</code></pre>
<ul>
<li><code>-t</code> creates a TTY</li>
<li><code>-i</code> captures STDIN</li>
</ul>
<p>...basically opens an interactive shell.</p>
<h2>Stopping & Deleting</h2>
<pre class="language-shell"><code class="language-shell"><span class="token function">docker</span> stop CONTAINER </code></pre>
<pre class="language-shell"><code class="language-shell"><span class="token function">docker</span> <span class="token function">rm</span> CONTAINER </code></pre>
<h2>Listing docker images</h2>
<pre class="language-shell"><code class="language-shell"><span class="token function">docker</span> images</code></pre>
<h2>Build a static container with my blog</h2>
<p>create the <code>Dockerfile</code>:</p>
<pre class="language-docker"><code class="language-docker"><span class="token comment"># Version: 0.0.1 </span><br /><span class="token instruction"><span class="token keyword">FROM</span> ubuntu:18.04 </span><br /><span class="token instruction"><span class="token keyword">LABEL</span> maintainer=<span class="token string">"XXX@gmail.com"</span></span><br /><span class="token comment"># update and install stuff we need: ruby, nodejs, nginx</span><br /><span class="token instruction"><span class="token keyword">RUN</span> apt-get update && apt-get install -y nginx make build-essential ruby ruby-dev curl</span><br /><span class="token instruction"><span class="token keyword">RUN</span> curl -sL https://deb.nodesource.com/setup_10.x | bash -</span><br /><span class="token instruction"><span class="token keyword">RUN</span> apt-get install -y nodejs </span><br /><span class="token comment"># install ruby gems to build the blog</span><br /><span class="token instruction"><span class="token keyword">RUN</span> gem install bundler</span><br /><span class="token instruction"><span class="token keyword">RUN</span> gem install jekyll</span><br /><span class="token comment"># specify that we want to work in the directory /root/</span><br /><span class="token instruction"><span class="token keyword">WORKDIR</span> /root/</span><br /><span class="token comment"># Remove the default Nginx configuration file</span><br /><span class="token instruction"><span class="token keyword">RUN</span> rm -v /etc/nginx/nginx.conf</span><br /><span class="token comment"># Copy a configuration file from the current directory</span><br /><span class="token instruction"><span class="token keyword">ADD</span> nginx.conf /etc/nginx/</span><br /><span class="token comment"># add files from our current directory (.) to the local work dir (/root/)</span><br /><span class="token instruction"><span class="token keyword">ADD</span> . .</span><br /><span class="token comment"># install jekyll plugins and build the site</span><br /><span class="token instruction"><span class="token keyword">RUN</span> bundle install </span><br /><span class="token instruction"><span class="token keyword">RUN</span> bundle exec jekyll build --config=_config.yml,_config_dev.yml</span><br /><span class="token comment"># copy generated website to nginx public dir </span><br /><span class="token instruction"><span class="token keyword">RUN</span> cp -r _site/* /usr/share/nginx/html/</span><br /><span class="token comment"># run nginx as foreground process. </span><br /><span class="token instruction"><span class="token keyword">CMD</span> [<span class="token string">"nginx"</span>, <span class="token string">"-g"</span>, <span class="token string">"daemon off;"</span>]</span><br /><span class="token instruction"><span class="token keyword">EXPOSE</span> 80</span></code></pre>
<p>In a Dockerfile there must be a <code>CMD</code> command otherwise the process will stop after being launched.</p>
<p>To actually make it work you should also configure your nginx with a valid <code>nginx.conf</code>:</p>
<pre class="language-nginx"><code class="language-nginx"><span class="token comment"># file nginx.conf</span><br /><span class="token directive"><span class="token keyword">worker_processes</span> <span class="token number">1</span></span><span class="token punctuation">;</span><br /><br /><span class="token directive"><span class="token keyword">events</span></span> <span class="token punctuation">{</span> <span class="token directive"><span class="token keyword">worker_connections</span> <span class="token number">1024</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br /><br /><span class="token directive"><span class="token keyword">http</span></span> <span class="token punctuation">{</span><br /> <span class="token directive"><span class="token keyword">include</span> mime.types</span><span class="token punctuation">;</span><br /> <span class="token directive"><span class="token keyword">sendfile</span> <span class="token boolean">on</span></span><span class="token punctuation">;</span><br /> <span class="token directive"><span class="token keyword">server</span></span> <span class="token punctuation">{</span><br /> <span class="token directive"><span class="token keyword">root</span> /usr/share/nginx/html/</span><span class="token punctuation">;</span><br /> <span class="token directive"><span class="token keyword">index</span> index.html</span><span class="token punctuation">;</span><br /> <span class="token directive"><span class="token keyword">server_name</span> localhost</span><span class="token punctuation">;</span><br /> <span class="token directive"><span class="token keyword">listen</span> <span class="token number">80</span></span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Then we can build the image:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> build <span class="token parameter variable">-t</span> <span class="token string">"musikele/blog"</span> <span class="token builtin class-name">.</span></code></pre>
<p>And we can run it with:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">-p</span> <span class="token number">80</span>:80 <span class="token parameter variable">--name</span> blog musikele/blog</code></pre>
<p>In case anything goes wrong, you can enter and inspect the running container with:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> <span class="token builtin class-name">exec</span> <span class="token parameter variable">-i</span> <span class="token parameter variable">-t</span> blog /bin/bash</code></pre>
The easiest way to understand Javascript Generators
2018-10-29T00:00:00Z
https://michelenasti.com/2018/10/29/the-easiest-way-to-understand-javascript-generators.html
<p><strong>Generators in Javascript are functions that return an iterator</strong>.</p>
<p><strong>Generators</strong> are functions declared with the keyword <code>function*</code>.</p>
<h2>What do you mean with "iterator"?</h2>
<p>The <em>iterator</em> is a design pattern usually already implemented in other programming languages. It is a <strong>data structure that allows you to <em>iterate</em> (read one by one) over the elements of the data structure.</strong></p>
<p>It might be very easy to think of an iterator for an array (a function that returns every element of the array, one by one), things become more complex if you want to iterate over a graph, or a tree.</p>
<h2>An Iterator example in plain Javascript (no generators here)</h2>
<p>An iterator obeys to these rules:</p>
<ul>
<li>The iterator must return a function that returns an object, containing a <code>next()</code> function.</li>
<li>This <code>next()</code> function, in turn, returns an object with two properties, <code>value</code> (the actual element of the iteration) and <code>done</code> (set to <code>true</code> when the iteration has ended, <code>false</code> otherwise).</li>
<li>This function is attached to the property <code>myObj[Symbol.iterator]</code> .</li>
</ul>
<p>Here is an example iterator for an array:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> clone <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// clones the original array</span><br /> <span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// iterating index; </span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> i<span class="token operator">++</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> clone<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">done</span><span class="token operator">:</span> i <span class="token operator">>=</span> clone<span class="token punctuation">.</span>length<br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>Let's try this in the nodejs console...</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> <span class="token keyword">let</span> iterator <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">,</span><span class="token number">7</span><span class="token punctuation">,</span><span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token keyword">undefined</span><br /><span class="token operator">></span> iterator<br /><span class="token punctuation">{</span> <span class="token literal-property property">next</span><span class="token operator">:</span> <span class="token punctuation">[</span>Function<span class="token operator">:</span> next<span class="token punctuation">]</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">7</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span></code></pre>
<p>I've already discussed this issue in <a href="https://michelenasti.com/2018/09/04/symbols-iterators-in-javascript.html" title="Symbols & Iterators in Javascript ">in another article</a>; there you'll find other examples and more explanations!</p>
<h2>Iterators are not funny.</h2>
<p>Nobody wants to write a functions that returns an object with a next property that returns a value.</p>
<p>That's why the Javascript community has introduced the generators syntax.</p>
<p>A generator is declared this way: <code>function* myGenerator {}</code> and this will return an iterator.</p>
<p>Let's rewrite the preceeding example with generators:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token keyword">this</span><span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">yield</span> <span class="token keyword">this</span><span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>You know that Promises interrupt the function execution, and when the promise resolves, the flow returns to the point it was left.</p>
<p><strong>Not only Promises but also Generators can interrupt the execution flow</strong>: when we return a value via the <code>yield</code> keyword, the generator function gets paused until it's called again.</p>
<p>Does it work?</p>
<pre class="language-javascript"><code class="language-javascript">$ node <br /><span class="token operator">></span> <span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><span class="token operator">...</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token keyword">this</span><span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><span class="token operator">...</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">yield</span> <span class="token keyword">this</span><span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token operator">...</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span><br /><span class="token operator">...</span> <span class="token punctuation">}</span><br /><span class="token punctuation">[</span>GeneratorFunction<span class="token punctuation">]</span><br /><span class="token operator">></span> <span class="token keyword">const</span> iterator <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//calling!</span><br /><span class="token keyword">undefined</span><br /><span class="token operator">></span> iterator<br />Object <span class="token punctuation">[</span>Generator<span class="token punctuation">]</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><br /><span class="token operator">></span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> <span class="token literal-property property">done</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span></code></pre>
<p>Yes, it works!!!</p>
<h2>But why Iterators are important</h2>
<p>JS uses iterators in <code>for ... of</code> loops, so if the object after the <code>of</code> contains an iterator, you can iterate over all the elements of the object.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token operator">></span> <span class="token comment">// The array here is using the iterator defined before...</span><br /><span class="token operator">></span> <span class="token keyword">for</span> <span class="token punctuation">(</span>element <span class="token keyword">of</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><br /><span class="token number">1</span><br /><span class="token number">2</span><br /><span class="token number">3</span><br /><span class="token number">4</span><br /><span class="token number">5</span></code></pre>
<p>It works!</p>
<h2>What else you need to know</h2>
<p><code>yield</code> must be in generator functions; you cannot put <code>yield</code> in subfunctions, or in promises results, asyncs, etc.</p>
<h2>Ready to use it ?</h2>
<p>Generators are widely supported by all major/modern browsers, <strong>except for IE</strong>.</p>
<p>A not well known JS feature; since it is part of the language specification, the more you know, the more you gain.</p>
Inspiring tech people to follow: Nicholas C. Zakas
2018-11-05T00:00:00Z
https://michelenasti.com/2018/11/05/inspiring-tech-people-to-follow-nicholas-c-zakas.html
<p>Usually in the software engineering world we talk about programming languages, tools and code. Let's start a different path with my blog and talk about people behind it!</p>
<p>For my first journey in <em>people</em>, I'll start with one of my very first guides in the javascript world: <strong>Nicholas C. Zakas</strong>.</p>
<p>Nicholas is a software engineer & architect, he has worked in super big companies like Yahoo and Box. He also wrote a lot of good books, like the one <a href="https://www.amazon.com/Professional-JavaScript-Developers-Nicholas-Zakas/dp/1118026691/ref=as_li_ss_tl?&linkCode=sl1&tag=nczonline-20&linkId=3910cb3b203837ce343949cac14f05d5&language=en_US" title=" Professional JavaScript for Web Developers ">I used to study in 2013</a> (DON'T BUY THIS BOOK - it's outdated now. It was a great book before ES6 and all the good stuff we know about js were in place).</p>
<h2>Successful Open Source projects</h2>
<p>Nicholas will be remembered for ages because he wrote one of the most successfull projects in the javascript community: <a href="https://eslint.org/"><strong>ESLint</strong></a>.</p>
<p><img src="https://michelenasti.com/images/eslint.png" alt="" /></p>
<p>ESLint - for those who might not know - is a linter. This means that catches eventual errors you may have introduced in your javascript code. There were other linters before ESLint, mainly JSlint and JSHint, but <strong>ESLint had something more: rules could be written and plugged by everybody</strong> (supporting new JS features in seconds, without waiting for the maintainer to release a new version). That's why it was adopted by React and all the other big frameworks of our community.</p>
<h2>Books</h2>
<p>Nicholas has also written some very influent books, and this gave him the opportunity to move to Silicon Valley and find him great jobs. I find his books extremely detailed and also very clear. These are two things that usually don't go together!</p>
<h2>Blog, mailing list</h2>
<p>If you want to see what Nicholas has been writing about in the latest years, He also has a blog called <a href="https://humanwhocodes.com/">Human Who Codes</a>. His latest blog post, at the moment of this writing, is this post about <a href="https://humanwhocodes.com/blog/2018/10/my-somewhat-complete-salary-history-software-engineer/">his earnings history</a>, useful to get an insight on salaries in Silicon Valley or USA. The intent of the post is to give women more knowledge about what should they earn.</p>
<p>But there is just more stuff going on his blog. I have also subscribed to his mailing list and when I received one of his emails I was thrilled to read it. I think his gift is to explain complex situations (like the ones he has lived on his own skin) to us, and to perfectly give context so deeply understand the point.</p>
<h2>Health Issues</h2>
<p>I'm writing this paragraph because Nicholas talks openly about his problem with the Lyme disease. It was such a huge problem that he had to stop working in 2013, even remotely, and just focus on his health. However he is recovering and he is writing new stuff on his blog.</p>
What I learned by writing my first npm module
2018-11-17T00:00:00Z
https://michelenasti.com/2018/11/17/what-i-learned-by-writing-my-first-npm-module.html
<p>Last weekend I wrote a simple module that converts a Javascript array in a HTML table. It's called <a href="https://www.npmjs.com/package/html-table-builder" title="HTML Table Builder">Html Table Builder</a>.</p>
<p>It is an exercise from the book <a href="https://eloquentjavascript.net/">Eloquent Javascript</a>; also, there are other gazillion modules on NPM that do the same thing with very similar names (a sign that the book is used a lot by javascript learners).</p>
<h2>A little description</h2>
<p>The library exposes a js function that accepts a js array and returns a HTML <code><table></code> object (not string!), so you can attach to the document with <code>document.appendChild(element)</code>. Checkout the <a href="https://github.com/musikele/html-table-builder">documentation on github</a>.</p>
<p>My idea was to release a library that was:</p>
<ul>
<li><strong>fully tested</strong></li>
<li>developed with <strong>Test Driven Development</strong></li>
<li><strong>isomorphic</strong> (by using <a href="https://www.npmjs.com/package/jsdom">JSDOM</a> on node)</li>
<li>built by <strong>webpack</strong></li>
<li>accepts objects with <strong>custom columns</strong></li>
</ul>
<p>Many more features have to come:</p>
<ul>
<li>apart from the fact that the input must be an array, I don't want to force users to pass array of objects. So you can pass an array containing strings, numbers, etc. even heterogeneous objects, the function will render it somehow.</li>
<li>I want to generate tables-in-tables, if the object in input is an array containing other arrays. This is tricky to do, I'll have to handle colspans and more.. but not impossible.</li>
<li>I also have to add css classes to columns (so you can style your tables)</li>
<li>...and add a showcase page. Who's gonna use your library if nobody understand how to use it?</li>
</ul>
<h2>I lost a lot of time with...</h2>
<ul>
<li>
<p><strong>Webpack</strong> - diving inside the webpack configuration to get the library minimized and bundled should be an easy task, BUT our friends in webpack have added thousands of options to configure every single bit.<br />
In the end this is <a href="https://github.com/musikele/html-table-builder/blob/master/webpack.config.js">the configuration that works for me</a>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// webpack.config.js</span><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">entry</span><span class="token operator">:</span> <span class="token string">'./index.js'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">target</span><span class="token operator">:</span> <span class="token string">'web'</span><span class="token punctuation">,</span> <br /> <span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">filename</span><span class="token operator">:</span> <span class="token string">'html-table-builder.js'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">library</span><span class="token operator">:</span> <span class="token string">'HtmlTableBuilder'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">libraryTarget</span><span class="token operator">:</span> <span class="token string">'umd'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">'production'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">devtool</span><span class="token operator">:</span> <span class="token string">'source-map'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">externals</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">jsdom</span><span class="token operator">:</span> <span class="token string">'JSDOM'</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /></code></pre>
<p>The relevant bits to expose the library are in the <code>output</code> property; read the <a href="https://webpack.js.org/configuration/output/#output-library">documentation</a> for it.</p>
</li>
<li>
<p><strong>NPM</strong> - I used npm (tool) to upload my pckage to NPM (site). NPM had some security issues lately, somebody hacked maintainers accounts and released dangerous code in popular libraries. That's why they reset all user passwords and implemented 2FA authentication, that is a good thing. It even works from console.
But the main trouble was naming the library. It is a table generator, and you really can't imagine <a href="https://www.npmjs.com/search?q=json%20to%20table">how many there are</a>. Trying to be self-explicative, without sacrificing the "ideal good name", took me to choose a <code>html-table-builder</code>. I would have loved to call it <code>super-table</code> or <code>table-generator</code>, but guess what? Already taken. Sad.</p>
</li>
<li>
<p><strong>Javascript</strong> - I have a story to tell about JS and dynamic typing. I was writing the function that <a href="https://github.com/musikele/html-table-builder/blob/master/get-columns/index.js">extracts the name of the columns</a>, and in the first version it returned an array of strings.<br />
Then I decided to return an array of object and DANG! errors everywhere, fortunately tracked down by unit tests.<br />
Then I decided to change again and make it return an object instead of an array, and again only TDD could help me to track down errors.<br />
In the end ... <strong>static types could have helped to catch errors without TDD</strong> and I could have used TDD-time to develop more complex tests.</p>
</li>
</ul>
<h2>What did not give problems</h2>
<ul>
<li><strong>Jest</strong>. Testing with Jest has been <em>extremely easy</em>. It was my first experience with Jest (coming from Mocha) but I have never had to checkout the documentation for anything. Good!</li>
<li><strong>ESLint, Prettier</strong> and <strong>VSCode</strong> - These tools resolve stupid issues for you. Just install and forget about them. They'll add semicolumns, fix tabs or spaces, return syntactic errors live... A good bonus.</li>
</ul>
<h2>Finally</h2>
<p>It is a very simple library, but it is already downloaded by 64 users (small number, but <em>great!</em> If you're a user, pass by and let me know your problems).</p>
<p>I learned a lot of stuff by writing this library; <strong>It took me some 8hrs to work it out</strong>. But it was fun.</p>
<blockquote>
<p>You remember 5% of what you read, 10% of what you write, 90% of what you do again.</p>
</blockquote>
<p>Exactly what I thought while I was preparing the first release!</p>
10 years around the web platform, what's changed and what's not
2018-12-12T00:00:00Z
https://michelenasti.com/2018/12/12/remembering-the-bad-bad-days-of-browser-wars.html
<p><strong>In 2006 I was studying web technologies at University</strong>. Avilable browsers were Firefox, Opera, Internet Explorer, Netscape on some linux computers.</p>
<p>![The web was a huge mass of <table> elements](/images/1 Me2MiUgum7fhYWoUp5nwJA.png "The youtube homepage in 2006")</p>
<p><strong>HTML</strong> was at version 4 and there were a lot of standards competing like <strong>xhtml</strong> (basically, xml to write html and help syntax checkers) and also many <strong>doctypes</strong> (strict, transitional ...) that I never fully understood. Fortunately now we just write <code><!DOCTYPE html></code> and that's it. <code><div></code> was a relatively new thing.</p>
<p><strong>Javascript</strong> was not cool. The teacher didn't spend more than 2-3 lessons on it, just explaining how to do basic input validation and some DOM manipulation. Strangely enough, javascript as a language was never versioned but now we refer to that days as EcmaScript3 (<a href="https://www.w3schools.com/js/js_versions.asp">here's a table with all javascript versions</a>). No JQuery was introduced. There were no books ot guides to learn JS, too. There was just one devtool available, and it was a Firefox extension called <em>Firebug</em> (later it became part of the browser).</p>
<p><strong>CSS</strong> was also crazy (it still is!). Some browsers adhered to the standard, but not the main one, forcing developers to cross test everything on all possible browsers. Fortunately at the time browsers were just 3-4, not the plethora we have now.</p>
<p>So... my first days of web design were just crazy. One of the tasks I had to do for this exam was to develop a "personal website", and I remember that it was not rendering correctly on IE.</p>
<h2>Internet Explorer, the bad guy</h2>
<p>What I didn't know is that IE <em>deliberately</em> violated the standard, using its power and market share to <strong>impose</strong> their version of the web against rivals.</p>
<p>I struggled realizing a simple website that would show correctly on firefox, following the standards, but not at all on IE (95% of the market share at the time).</p>
<p>Same for Javascript: IE was just not following some rules that were correctly implemented by others, or deliberately not implementing some standard features. This is one of the motivations for libraries like <strong>JQuery</strong> to rise: cross-browser uniformity.</p>
<p>I promised to myself to never work on the web: the platform is unreilable, buggy, nothing works as expected and there's nothing you can do about it, except than doubling the work.</p>
<h2>Fast forward, 12 years later</h2>
<p>I'm working as a web developer since 6 years, basically I had no choice: when I ended university all the companies were transforming their long-lived java applications to web applications. The good news are that javascript has been standardized (ES5 before, and ES6 after, were huge steps towards browser uniformity) and even newer versions of IE are supporting it.</p>
<p><strong>Chrome</strong>, started in 2008, is now the market leader with about 68% of the market share. Chrome uses its own rendering engine called <em>Blink,</em> part of the <em>Chromium project</em>.</p>
<p><strong>Firefox, the only truly indipendent browser,</strong> is my default browser. They are not developed by a company but by a no-profit foundation. Their goal is to make the web platform the best possible platform. For many years they were behind Chrome on features, speed and privacy, but now FF is just the same if not better. So yes, I'm using Firefox for moral issues, but there's litterally nothing that I'm loosing since the switch. (I also use FF on mobile). Firefox uses its own rendering engine called <em>Gecko</em>.</p>
<p><strong>Edge</strong> was a new browser by Microsoft, and it was a surprisingly great browser. It is very fast and it also has a lot of features dedicated to tablets and touchscreens, so that you may prefer it on portable devices. Edge is still not widely adopted (lates stats say that edge is at 4% while IE is <em>still</em> at 11%!) but it should. Edge uses it's own rendering engine called <em>EdgeHTML</em>.</p>
<p><strong>Internet Explorer</strong> in 2018 it should be a thing of the past, but it's not. IE has been <em>deprecated</em> and there will be no support for it in the future. It still has 11% of global market share (In Italy, where I live and work, it's at 2-4%) and I think this happens because many companies impose to their employees to use IE, since they may have developed security features, of custom applets, or other stuff that cannot be thrown away and rebuild in newer technology because it's not a simple/cheap job. This is what happens when you entangle your company on a closed platform...</p>
<p><strong>Safari</strong> is a browser that only exists on Apple iDevices. Safari is now considered the IE of the current browsers: many missing features, slow updates, slow fixes... Many standard components are simply ignored because it's not functional to the company's view of the web (Apple encourages developers to go native). Safari is based on <em>Webkit</em>.</p>
<p>There are many other browsers, like <strong>Opera</strong>, <strong>Brave</strong>, etc. but they've not reached great market share, even if they are technologically very valid.</p>
<p><img src="https://michelenasti.com/images/1472784019browser-wars-over.png" alt="" /></p>
<h2>So ... what should we do now</h2>
<p>The web is the platform that I know better: I love and hate it. <strong>I love the fact that it's spontaneous</strong>, with many great ideas going on, and it also moves very fast so that's impossible to stay current with every possible update.</p>
<p><strong>I hate that some companies implement standards based on what they consider useful.</strong> This has happened for a decade with Internet Explorer, and now with Safari.</p>
<p>With Chrome I have a difficult relationship: <strong>I don't like that one company owns the future of a browser, and with a 70% of the web they also own the future of the web</strong>; it creates some moral questions about privacy and security, too. But <strong>we cannot negate that Google has done a great job</strong> by open sourcing big parts of the browser and they also have been the first browser to implement new standards (and closing bugs).</p>
<p>The latest news are that <strong>Edge is switching to a Chromium-based rendering engine</strong>. They are doing this because Edge was so new that - like all new things - the browser was crashy, with bugs, and not performing well on some things that others do just fine. I am not an Edge user and, while I have to admit that technologically they are chosing the best available platform, I feel sad that another competitor gives away the competition. <strong>This brings us to a monopoly and that's not what I expect</strong>. The good thing is that developers at Microsoft will start contributing to parts of Chromium, so the rendering engine will not be really owned by only one company, but we still don't know the direction of these two companies and their long-term plans.</p>
<h2>Please condiser using a non-Chromium browser for some time</h2>
<p>I'm already using and supporting <strong>Firefox</strong> and there's literally nothing I miss. It's fast, secure, privacy oriented, cool. Break the monopoly; <strong>in the long term we - users - will be the only ones to earn the benefits of this choice.</strong></p>
Another remote job for me
2019-01-17T00:00:00Z
https://michelenasti.com/2019/01/17/another-remote-job-for-me.html
<p>Two years ago I wrote about <a href="https://michelenasti.com/2016/06/cambio-lavoro-alla-scoperta-del-remote-working/">quitting a job in a big italian corporation</a> to join a smaller reality that worked in fintech.</p>
<p>My previous company gave me an amazing experience. I worked on two fantastic public-facing products, a <strong>payments gateway</strong> and a low-cost POS solution for small businesses.</p>
<p>However after two years I felt like I <em>had</em> to change job, to make new experiences.</p>
<h2>How ?</h2>
<p>Long story short, <a href="https://michelenasti.com/2018/09/19/Javascript-chiamare-funzioni-senza-usare-parentesi-(what!).html">I wrote an article on my blog that had an impredictable success</a> about an obscure javascript feature and <strong>some companies reached out to know weather I wanted to join them</strong>.</p>
<p>I did not send any CV in thist first phase. I think they understood my skills from my blog. I'm writing this because I'm super happy that my blog did pay off. It started as a (cheap) long-term investiment and I'm happy it succeded.</p>
<p>Back to the job, a guy reached out from Switzerland, writing to me in italian, asking weather I wanted to have a chat about his company. There was a lot of empathy with this guy, and I evaluate empathy as a bonus factor so when his boss finally asked me to go and have a meeting in person, I accepted.</p>
<h2>The interview</h2>
<p>So I took a plane one morning and flied to Zurich. The interview was a classic whiteboard interview, and the question was easy (the difference of the two sum of two diagonals of a matrix) but this was just a starting point. We went through writing pseudocode, optimizing, etc. I remember I did not answer very well a question about how DNS works (a critical component in their architecture) but in the end they still hired me.</p>
<h2>What they do</h2>
<p>The company works in the Advertising space. Their main job is to deliver ads to website visitors, like Adsense; but they organize an auction with many other ad providers, and this happens in the browser, so that the best-paying ads are shown.</p>
<p>So publishers contact the company and ask them to deliver ads to visitors, by injecting some javascript code. Then, revenues are payed from my company to the websites. At the moment they have clients spanning in the whole US market.</p>
<h2>Programmatic advertising</h2>
<p>In the latest years a new branch in advertising was born, called <strong>Programmatic Advertising</strong>. Programmatic uses a new approach to deliver ads, <strong>Header bidding</strong>, and tries to make the website earn as much as possible by delivering the most valuable ad from a variety of sources. It's way more interesting, complex, asynchronous and fast than you think. The web is basically free because of ads. Delivering good ads as fast as possible it's super important: most ads you see on any website are rendered before the DOM is even parsed!</p>
<h2>So ... when do you start?</h2>
<p>Yes! I'm already working with them. I'm studying all the parts involved in the advertising world (DSPs, SSPs, DFP, GTP... these acronyms make no sense to those who do not work in this area!) and I am already deep diving in the typescript code. So many things happen in one library.</p>
Is typescript Namespace feature deprecated?
2019-01-23T00:00:00Z
https://michelenasti.com/2019/01/23/is-typescript-namespace-feature-deprecated.html
<p>At my new job I maintain a medium-sized typescript library that is critical to the success of the company.</p>
<p><img src="https://michelenasti.com/images/typescript-cover-image.jpg" alt="" /></p>
<p>The guy who wrote the library was not expert in typescript (but he was in ES6) and he needed a way to manage complexity before it was too late; so he rewrote entire parts of the app using <strong>typescript namespaces</strong>.</p>
<h2>What is a namespace in Typescript</h2>
<p>Apart from the fact that typescript actually has a good documentation about <a href="https://www.typescriptlang.org/docs/handbook/namespaces.html" title="Typescript Namespaces">namespaces</a>, it is not clear why you would need them in first place. So, a namespace is declared like this:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">namespace</span> Validation <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span></code></pre>
<p>inside the namespace you can create wathever you need: classes, functions, etc.</p>
<p>What's the corresponding ES5 output?</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>Validation<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><br /> Validation<span class="token punctuation">.</span>foo <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span><br /><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>Validation <span class="token operator">||</span> <span class="token punctuation">(</span>Validation <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre>
<p>The magic happens in the arguments: we pass the <code>Validation</code> object if exists, otherwise we initialize a new <code>Validation</code> object. This way we can add as many object we want to the same namespace without polluting the global scope.</p>
<p>But ...</p>
<h2>Is this actually needed?</h2>
<p>You can actually have many files that declare objects and classes in the same namespace:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token comment">// typescript: </span><br /><span class="token comment">// file 1</span><br /><span class="token keyword">namespace</span> Validation <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token comment">// file 2</span><br /><span class="token keyword">namespace</span> Validation <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> bar <span class="token operator">=</span> <span class="token number">567</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//This becomes, in es6:</span><br /><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>Validation<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> Validation<span class="token punctuation">.</span>foo <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>Validation <span class="token operator">||</span> <span class="token punctuation">(</span>Validation <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br /><br /><span class="token comment">//file 2... </span><br /><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>Validation<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> Validation<span class="token punctuation">.</span>bar <span class="token operator">=</span> <span class="token number">567</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>Validation <span class="token operator">||</span> <span class="token punctuation">(</span>Validation <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre>
<p>And in memory <code>Validation</code> will contain both <code>foo</code> and <code>bar</code> properties.</p>
<p>If these two <code>Validation</code> namespace declaration are in two separate files, evaluating order is important. That's why in Typescript when you use namespaces you also use a custom tag <code>/// <reference path="Validation.ts" /></code> to reference the actual ordering of files. But, guess what? <strong>This tag is not supported by Visual Studio Code</strong> - no autocomplete, bitches.</p>
<p>Apart from this, the problem we faced was <strong>testing</strong>: actually being able to test a namespaced class or function is very important (where is quality if you don't have tests?). We eventually found the combination of importing, module type and typescript setting that would allow us to do this. but it was cumbersome.</p>
<p>I <em>think</em> that this syntax was developed before ES6 was actually developed, but in my modest opinion <strong>ES6 modules surpassed them in functionality</strong> and are more similar to how we commonly modularize stuff. You should go with ES6 straight.</p>
<h2>Why ES6 modules are better</h2>
<p>First, an example:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token comment">//typescript</span><br /><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Validaton</span> <span class="token punctuation">{</span> <br /> <span class="token operator">...</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//becomes, in javascript:</span><br /><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Validation</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//then you can import the class anywhere: </span><br /><span class="token keyword">import</span> <span class="token punctuation">{</span> Validation <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../Validation'</span> <br /><br /><span class="token keyword">function</span> <span class="token function">checkData</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> Validation<span class="token punctuation">.</span><span class="token function">isValid</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>Can you see the difference? There is <em>NO</em> difference! Typescript is a superset of ES6 so you have more power in what you can do, but that doesn't mean you should do it. In this case, ES6 solved the problem of modularization for us by doing a good job, so we don't need any namespace at all in common programs (unless you specifically want to use namespaces).</p>
<p>My rule of thumb: <strong>stick to ES6 the more you can and just add types</strong> to classes and functions. This way the code is readable also by those who don't know typescript.</p>
<p>The great <strong>benefit</strong> of all this is with <strong>IDE autocompletion</strong> and <strong>error highlighting</strong>. Today I refactored the whole library based only on IDE suggestions, and it was a breeze. Can you imagine doing this in ES6? Impossible.</p>
<h2>Other people that deprecates namespaces</h2>
<p>This <a href="https://basarat.gitbooks.io/typescript/docs/project/namespaces.html">ultra-famous book</a> about typescript states:</p>
<blockquote>
<p>For most projects we recommend using external modules and using <code>namespace</code> for quick demos and porting old JavaScript code.</p>
</blockquote>
<p>TSLint has a predefined rule to <a href="https://palantir.github.io/tslint/rules/no-namespace/">avoid namespaces</a>.</p>
<p>The same opinion is found in <a href="https://stackoverflow.com/questions/12737942/does-typescript-support-namespace#comment78314603_12742162">many places</a> around Stack Overflow:</p>
<blockquote>
<p>I wouldn't recommended <code>namespace</code> nor mixing it with <code>module</code> source code.</p>
</blockquote>
<h2>So what</h2>
<p>I think namespaces will hit their end-of-life soon, basically they solved a problem that now can be solved with classical ES6 syntax. Even if the import/export keyword is not working in all browsers out of the box, that' the road they are covering so it's just a matter of time and we'll get rid of any other solution we had to manage complexity.</p>
<p>See you soon!</p>
How ads are loaded so fast: the command queue pattern
2019-02-12T12:00:00Z
https://michelenasti.com/2019/02/12/how-ads-are-loaded-so-fast-the-command-queue-pattern.html
<p>Prior to work in an ads company I had no idea of how ads are actually rendered on a page. I mean, I had a vague sense of what was going on, but the actual world behind it ... well, it's huge :)</p>
<p>Sometimes ads are loaded on a page even before you actually see any content. How? The first thing we learned at university is to wait for the DOM to have completely loaded, before doing anything to the DOM itself. This does not apply to ads, or better to say, ad companies use many tricks to load ads without blocking the DOM.</p>
<p>I won't go in the detail of this process now, but basically <strong>ads are rendered inside <em>iframes</em></strong>, and iframes should be used only for very narrow use cases, like widgets to inject on other pages, or payment processors. In the past I did the exact opposite: we implemented an angular 1 routing system with iframes, and that's something I wouldn't do again (<a href="https://michelenasti.com/2015/05/iframe-safari-ios-e-la-lotta-allultimo-millisecondo/">my experience</a> - in italian).</p>
<p>Here in this article I'm going to talk about how some popular ad libraries (like <a href="https://support.google.com/admanager/answer/1638622?hl=en&ref_topic=4390039">google publisher tag</a> or <a href="http://prebid.org/dev-docs/getting-started.html">Prebid</a>) allow users to write code that will be put in a queue and will be executed as soon as the library is loaded.</p>
<h2>The problem: async library loading</h2>
<p>Suppose we have a library that will load something, and this initialization process will take some time. Also, we don't know when the library will be actually loaded, since it's asyncronous. However, we would like to start giving commands (e.g. setup instructions) to this library as soon as possible. How?</p>
<blockquote>
<p>Let's suppose our library's name is, without much fantasy, <code>Library</code> and is loaded from file <code>Library.js</code>.</p>
</blockquote>
<h2>The client code</h2>
<p>in the client's code, the code that the user will write to interact with our library, we will initialize the library like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">var</span> Library <span class="token operator">=</span> Library <span class="token operator">||</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <br />Library<span class="token punctuation">.</span>queue <span class="token operator">=</span> Library<span class="token punctuation">.</span>queue <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre>
<p>In these two simple lines, we have initialized our library and its commands queue. In fact, if the <code>Library</code> object does not exist, we will initialize it with an empty object, and then we will create the empty array property <code>Library.queue</code> that will contain our initalization code. For example:</p>
<pre class="language-javascript"><code class="language-javascript">Library<span class="token punctuation">.</span>queue<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Called only when the library has loaded, not before"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Library<span class="token punctuation">.</span><span class="token function">doMagic</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<blockquote>
<p>Note: I'm deliberately using ES5 code here. It would be fantastic that every user in the world was using the latest version of the coolest browser, but the reality is that <strong>a bunch of zombies are still using Internet Explorer</strong>. Ad companies want to earn money on these people too!</p>
</blockquote>
<h2>How we load the Library</h2>
<p>Imagine we load our library with this <code>script</code> tag:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/path/to/Library.js<span class="token punctuation">"</span></span> <span class="token attr-name">async</span> <span class="token attr-name">defer</span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<p><code>async</code> means that the browser will download the library as soon as possible, but the evaluation will start whenever it is more convenient (the browser decides). The HTML parser is paused when the script is evluated. <code>defer</code> means the same thing (more or less!) but the code execution happens only after the DOM has been loaded. <a href="https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html" title="async vs defer">More info here</a>.</p>
<p>By inserting the <code>async</code> and <code>defer</code> keyword we don't have any guarantee of the Library execution time, so we don't know who will be loaded first.</p>
<h2>The Library internals</h2>
<p>Here's an example of how the Library could initialize itself:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//This code could be in file Library.js </span><br /> <br /><span class="token keyword">var</span> Library <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token keyword">var</span> queue <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>Library<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// queue from outside might be null... </span><br /> queue <span class="token operator">=</span> Library<span class="token punctuation">.</span>queue <span class="token operator">||</span> queue<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <br /> <span class="token comment">//here we execute code that is in the queue</span><br /> <span class="token keyword">while</span> <span class="token punctuation">(</span>queue<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> command <span class="token operator">=</span> queue<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <br /> <span class="token function">command</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>First we will check if the <code>Library</code> object already exists. This may be because of the initialization we did in the client code. If so, we take the queue object and start executing all commands from the queue, until the queue is empty.</p>
<h2>That's so async, it's actually fast</h2>
<p>By not forcing the browser to stop and parse our js, we gain a significant amount of perceived speed. And by splitting the client code in <em>commands</em> we also get the guarantee that the client code will be executed.</p>
<p>In this simple snippet we have not covered other themes like:</p>
<ul>
<li>what if we add another element to the queue after the initialization has already completed?</li>
<li>A way of logging functions (yes, we can stringify functions) and remember a history of executed functions</li>
</ul>
<p>That's an exercise for you, fellow readers!</p>
What's the difference between Browserify and Webpack?
2019-03-23T00:00:00Z
https://michelenasti.com/2019/03/23/what-s-the-difference-between-browserify-and-webpack.html
<p><strong>Browserify</strong> belongs to the very first generation of module bundlers and it basically allowed one thing, that is, use the <code>require</code> function from NodeJS in the browser. Browserify's only job was to concatenate files wrapping them in functions that were then called by other fils.</p>
<p><img src="https://michelenasti.com/images/bropack.png" alt="" title="Browserify vs Webpack" /></p>
<p><strong>Webpack</strong> came around after (that's why I call it <em>2nd generation</em>) but it enhanced a lot and it has also joined the <em>3rd generation</em>, together with <strong>Rollup</strong> and <strong>Parcel</strong>. The difference with Browserify is that Webpack is extremely configurable, and it can be used to write libraries, webapps, and much more.</p>
<p>Webpack integrates so well with all existing tools that already existed but that didn't talk to each other (minimizers, obfuscators, patchers...) and by integrating with <strong>babel</strong> it allowed to write ES6 code even before browsers were actually supporting it.</p>
<p>The third generation not only did all of this but also introduced the concept of <em>zero config</em>, meaning you don't have to configure anything, it will just work.</p>
<p>And then there's <em>threeshaking</em>: if an external file exposes 100 functions but you use only one, webpack picks only the one you used and its dependencies, not the whole file. This way, your bundler will save space, meaning less data flowing through the net.</p>
SSH cheatsheet (from zero to hero)
2019-04-03T00:00:00Z
https://michelenasti.com/2019/04/03/ssh-cheatsheet-from-zero-to-hero.html
<p>SSH is one of the basic commands you need to master, no matter what's your platform of choice. Because you'll always end up working on some random server, and you'll have to login to see logs or restarting services. Also, you'll never see it as a <em>skill to show</em> because it's implicit that you have to know how to use it, at least for logging in to some website.</p>
<p>Here is a little cheatsheet of the most important commands (and tricks) you might find useful.</p>
<p><img src="https://michelenasti.com/images/ssh-big.png" alt="" /></p>
<h2>Creating a new pair of keys</h2>
<p>Before working with SSH, you need to create a new pair of keys. Basically it is a pair of private/public keys. <strong>To generate a new key pair on your computer</strong>, run:</p>
<pre class="language-bash"><code class="language-bash">$ ssh-keygen</code></pre>
<p>keys are created in <code>~/.ssh</code> directory. Two files will be created:</p>
<pre class="language-bash"><code class="language-bash">➜ $ <span class="token builtin class-name">cd</span> ~/.ssh<br />➜ $ <span class="token function">ls</span> <span class="token parameter variable">-al</span><br />-rw------- <span class="token number">1</span> musikele musikele <span class="token number">1</span>,8K feb <span class="token number">18</span> <span class="token number">21</span>:56 id_rsa<br />-rw-r--r-- <span class="token number">1</span> musikele musikele <span class="token number">411</span> feb <span class="token number">18</span> <span class="token number">21</span>:56 id_rsa.pub<br />➜ $</code></pre>
<p>The private key is <code>id_rsa</code> and you should never share it with anybody. It is readable and writable only by the current user.</p>
<p>The public key is <code>id_rsa.pub</code> and it can be shared with others. As you can see the <code>id_rsa.pub</code> is also readable by anyone on the system.</p>
<h2>Set up SSH login to remote server without password</h2>
<p>Now that we have SSH set up on our computer, let's see how we can login to a remote server. (We assume that ssh is set up on the remote server too - if not, just lunch the previous command there too!).</p>
<p>There are two ways to log in to a remote server:</p>
<ul>
<li>by typing the remote user's password,</li>
<li>by using your private/public key.</li>
</ul>
<p>We will talk about the second option from now on.</p>
<p>What we want to do now is to register our public key on the server. I want to remain practical in this article, but if you need some deep explanation of what happens during the login process I suggest you to <a href="https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process" title="How SSH works">follow this link</a>.</p>
<p>There's a simple command to set up the ssh key on a remote server (run on your local computer):</p>
<pre class="language-bash"><code class="language-bash">$ ssh-copy-id root@123.123.123.123</code></pre>
<p>once you hit enter, the remote server will ask for the password (at least for the first time). Once done, you can log in to a remote server by typing</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> root@123.123.123.123</code></pre>
<p>...and you're in!</p>
<p>What is this command <code>ssh-copy-id</code> doing? This command will copy your public key (<code>~/.ssh/id_rsa.pub</code>) in the remote file <code>~/.ssh/authenticated_keys</code>. The server will use the your public key to authenticate you.</p>
<h2>Logging in as a different user</h2>
<p>When we log in to a remote server, without using a username, we will log in with the same username of our local machine. My username is <code>musikele</code>, so if I try to login to a remote server that's what happens:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> <span class="token number">123.123</span>.123.123<br />musikele@123.123.123.123's password:</code></pre>
<p>... but if you're logging in to a corporate machine you don't have a user set up with your username. So we prefix the host address with the remote user, like this:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> remote_user@123.123.123.123</code></pre>
<p>We're in!</p>
<p>Other useful options:</p>
<ul>
<li><code>-p 2222</code> is used to specify the port to use. Default port for SSH is 22.</li>
<li><code>-i /path/to/alternate/key</code> is used to speficy another <em>private</em> key you want to use instead of the default one. You can have as many public/private keys you want, and they an be in different files or in different paths.</li>
<li><code>-f</code> runs ssh in the background (you'll see later when to use it)</li>
<li><code>-N</code> does not open a window.</li>
</ul>
<p>to run only one command and exit, simply write the command after the ssh connection string:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> <span class="token parameter variable">-p</span> <span class="token number">2222</span> remote_user@123.123.123.123 <span class="token function">ls</span> <span class="token parameter variable">-al</span><br />drwxrwxrwx+ <span class="token number">3</span> root root <span class="token number">4096</span> Apr <span class="token number">3</span> <span class="token number">10</span>:02 <span class="token comment">#recycle</span><br />drwxr-xr-x <span class="token number">31</span> admin <span class="token function">users</span> <span class="token number">4096</span> Mar <span class="token number">13</span> 07:41 <span class="token builtin class-name">.</span><br />drwxrwxrwx+ <span class="token number">13</span> root root <span class="token number">4096</span> Feb <span class="token number">6</span> 09:54 <span class="token punctuation">..</span><br />-rwxrwxrwx+ <span class="token number">1</span> admin <span class="token function">users</span> <span class="token number">14340</span> Jun <span class="token number">18</span> <span class="token number">2017</span> .DS_Store<br /><span class="token punctuation">..</span>.<br />$</code></pre>
<p>We just lunched <code>ls -al</code> on a remote machine! (and the prompt at line 7 is our local prompt, again).</p>
<h2>Simplify connections with ssh config files</h2>
<p>It may be tedious to write the same info (username, remote server address, port...) again and again. With ssh we can define an alias with some informations already set, so that we don't have to type them again and again. Here's an example:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">vi</span> ~/.ssh/config</code></pre>
<p># use four spaces to indent
Host foo
Hostname 123.123.123.123
User root
IdentityFile ~/.ssh/id_rsa
Port 22</p>
<p>And then we just log in with</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> foo</code></pre>
<p>This is a real time saver tip ;)</p>
<h2>Copy files to and from the remote host</h2>
<p>One of the most common actions we want to do with our remote servers is to copy files from and to it. The handy command <strong>scp</strong> will help us doing this.</p>
<p>To transfer a file from local host to a remote one:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">scp</span> bar.txt mark@123.123.123.123:~/</code></pre>
<p>Here we are coping <code>bar.txt</code> file on the server, logging in as user <code>mark</code>.</p>
<p>Whatever is after the <code>:</code> is the path on the remote server. If the path starts with <code>/</code> it is an absolute path; otherwise it will be local to the user's home folder.</p>
<p>Some other handy options for this command are:</p>
<ul>
<li><code>-P</code> for the port (note that for the regular ssh we used <code>-p</code> lowercase)</li>
<li><code>-r</code> for a recursive copy of a folder</li>
</ul>
<p>And of course, if you want to copy from a remote host to your local pc:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">scp</span> mark@123.123.123.123:~/bar.txt ./new.txt</code></pre>
<h2>Configure a ssh tunnel</h2>
<p>With <em>tunneling</em> you can redirect traffic from one port of an ssh host to a remote server.</p>
<p>Let's see an easy example:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">ssh</span> <span class="token parameter variable">-L</span> <span class="token number">8000</span>:yahoo.com:80 mark@myhost.com</code></pre>
<p>In this case:</p>
<ul>
<li>your computer will listen on port <code>localhost:8000</code></li>
<li>when a packet is sent at <code>localhost:8000</code> it will reach <code>myhost.com</code></li>
<li>ssh daemon at <code>myhost.com</code> will redirect to <code>yahoo.com:80</code></li>
<li>responses follow the same path.</li>
</ul>
<p>This technique can be useful to access a server on a private network. The only problem is that we also log in to <code>myhost.com</code> and the connection stays open until we exit from the remote session.</p>
<p>Combining with options <code>-f -N</code> , we run the tunnel and return to the localhost computer:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">ssh</span> <span class="token parameter variable">-f</span> <span class="token parameter variable">-N</span> <span class="token parameter variable">-L</span> <span class="token number">8000</span>:yahoo.com:80 mark@myhost.com</code></pre>
<p>Another great use for tunneling is to redirect traffic from the ssh server to your local host. Imagine to hit <code>myhost.com:8000</code> but the traffic is redirected to <code>localhost:3000</code>. This is useful for debugging, or to set up proxies, etc.</p>
<p>Remote tunneling is disabled by default; to enable, open the config file:</p>
<p>$ vi etc/ssh/sshd_config</p>
<p>set <code>GatewayPorts</code> option to <code>yes</code> and restart the service (for example with <code>service ssh restart</code> on Debian/Ubuntu systems).</p>
<p>Now we can explore the tunneling functionality by launching:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">ssh</span> <span class="token parameter variable">-R</span> <span class="token number">8000</span>:localhost:3000 mark@myhost.com</code></pre>
<p>What's happening here:</p>
<ul>
<li>port <code>8000</code> on server <code>myhost.com</code> is exposed (be sure that it's reachable, for example by setting port forwarding on gateways if you have)</li>
<li>when you connect to port <code>myhost.com:8000</code> the data is sent to <code>localhost:3000</code></li>
<li>eventual responses will flow back on the same route.</li>
</ul>
<h2>Escape sequences</h2>
<ul>
<li>To stop a blocked ssh connection hit <code><ENTER></code> then write: <code>~.</code> (tilde fullstop).</li>
<li>Another escape sequence is <code>~ CTRL-Z</code> (tilde character + CTRL + Z). The ssh connection will be moved to background. To resume, type <code>fg</code>. Remember to hit <code><ENTER></code> if you have written anything before.</li>
</ul>
<h2>Verify SSH fingerprints</h2>
<p>When you connect to a new machine, a new entry is created in the local file <code>~/.ssh/known_hosts</code> of your computer. This entry contains the fingerprint of the server, so next time it will be already trusted.</p>
<p>However, sometimes keys on remote servers will change and our machine will not be able to connect again.</p>
<p>In order to check the new fingerprint of a remote server:</p>
<ul>
<li>connect to the remote server (e.g. <code>ssh root@123.123.123.123</code>) with password</li>
<li>on remote host, launch:</li>
</ul>
<pre class="language-bash"><code class="language-bash">$ ssh-keygen <span class="token parameter variable">-l</span> <span class="token parameter variable">-f</span> /etc/ssh/ssh_host_ecdsa_key.pub</code></pre>
<p>The output of the command is the fingerprint of the key.</p>
<ul>
<li>on localhost, launch:</li>
</ul>
<pre class="language-bash"><code class="language-bash">$ ssh-keygen <span class="token parameter variable">-R</span> <span class="token number">123.123</span>.123.123</code></pre>
<p>This will remove the line associated with <code>123.123.123.123</code> in the <code>~/.ssh/known_hosts</code> file.</p>
<ul>
<li>finally, connect again to the remote server:</li>
</ul>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> root@123.123.123.123</code></pre>
<p>The remote host will show its fingerprint, and it should match the one calculated before.</p>
<h2>Other cool SSH options</h2>
<h3>Deny any root access</h3>
<p>You can imagine why a ssh connection for the root account can be a bad thing. Fortunately It can be disabled.</p>
<ul>
<li>launch <code>vi /etc/ssh/sshd_config</code>. This file contains a bunch of options for ssh.</li>
<li>Search for <code>PermitRootLogin</code> and set to <code>no</code> to avoid root login.</li>
</ul>
<h3>Prohibit password access</h3>
<p>Another value for this setting is <code>prohibit-password</code>. This way you can only connect via public/private key. You can disable password authentication by setting <code>PasswordAuthentication</code> option to <code>no</code>.</p>
<p>If you want to accept connections only for a specific set of users, or only users that come from a specific IP, you can set <code>AllowUsers</code> option like this:</p>
<p>AllowUsers foo bar@1.2.3.4 *@2.3.4.5</p>
<p>Remember to restart the server with <code>service ssh restart</code>.</p>
<h2>Monitoring connection attempts</h2>
<p>To check malicious/suspicious activity we have some tools that come at help.</p>
<ul>
<li><code>vi /var/log/auth.log</code> contains all the informations about who tried to log in the system, with other info like the IP, etc.</li>
<li>command <code>lastlog</code> will show last logs from all users of the system.</li>
<li><code>lastlog -u mark</code> will display last logs for user <code>mark</code>.</li>
</ul>
<p>For example, we may see that our employee <code>mark</code> is connecting to the server, but at strange hours. So we can ask mark to <code>cat ~/bash_history</code> and check his latest commands. We can use this info to check if he legitimately accessed the machine, or if it was a hacker.</p>
<h3>That's all folks</h3>
<p>This article is nothing more than a summary of what I learned by following <a href="https://egghead.io/courses/ssh-for-remote-server-authentication">this excellent ssh course from Egghead</a>. I think they are great and deserve a paid subscription.</p>
I work remote, I do two video meetings per day
2019-06-13T00:00:00Z
https://michelenasti.com/2019/06/13/i-work-remote-i-do-two-video-calls-per-day.html
<p>Do you love meetings? Do you enjoy taking a break from your work and <em>talk</em> - ehm, <em>listen</em> to your managers, stakeholders, customers, peers? I suppose you not. If you're like me, 99% of your work life meetings have been a total waste of time.</p>
<p>For example, I remember one day I had 8 hours of meetings, well actually they were 4 different meetings of 2 hours, but with the same people and the same manager attending.</p>
<p>I also remember a boss sentence, "<em>we will discuss later about this, and we will, because this topic is important for me"</em>, usually meaning that we should never ever bring the discussion back.</p>
<p>I also remember another meeting, on friday evening, were we decided to use framework "X" to do something, so I started studying it in the weekend, only to find on monday that the company has decided to use another framework Y.</p>
<p>The list can go on, feel free to add your horror stories in the comments. I'm sure you had worse meetings than mine :)</p>
<p>But I'm here to say that I've also experienced another way of working: for some projects I had no meetings at all.</p>
<p>For example, referring to my remote work experience, when you start working remotely the first sentiment you feel is <em>abandonment</em>. It's like, <em>where's my boss?</em> Obviously I was not <em>abandoned</em> but that's the first feeling you get.</p>
<p>Then I read a book about <a href="https://amzn.to/2RdiUUt" title="Human centered work">human centered work (in italian)</a>, and amongst other things there is an entire chapter on how to organize the team work. <strong>Before calling your next meeting, ask yourself if all the attendees are actually getting a benefit from it</strong>. (With this simple rule, 50% of my meetings could have been discarded).</p>
<p>Some meetings are strictly necessary. <strong>The main role of a boss is to give the <em>strategic direction</em> to the team</strong>, and there's no email that can substitute the facial expression or the tone of voice.</p>
<p>So, let's go back to the title of this post. Yes, I work remotely, and yes, <strong>I do two</strong> (small) <strong>meetings every day</strong>. Two video-meetings actually. The first one at 9:30 is to kick off the day, the second at 15:30 as a status update.</p>
<p>They don't last long, usually in 5 minutes they are done, but it's a ritual that helps me accomplish my tasks.</p>
<p>The best part is that we use a software where we can see each other in the face during the meeting, and the default is with webcam on. This actually gives a lot of humanity to remote working: as programmers, we are used to solve problems via chat, by typing, but that's the slowest way to do that.</p>
<p>When you start introducing video calls as an option, and switch to that whenever it's the clearest (& fastest) way to solve a problem, you'll see the benefit.</p>
<p>So ... should your company do video meetings every day? If your team is small enough, and you all work in the same time zone, why not. I suppose this approach doesn't scale at all, but I still have to work for a big remote company so I don't know how they organize their work. As a programmer, I like to see that managers - and peers - are interested in what I'm doing, and doing a meeting is the bare minimum for showing appreciation.</p>
Typescript: why so complicated?! (A list of my preferred options)
2019-06-24T00:00:00Z
https://michelenasti.com/2019/06/24/typescript-why-so-complicated.html
<p>Hey there, after digging about how Typescript settings mesh together, I decided to write my article about what I found <em>the hard way</em>, hoping to clear the path for newcomers.</p>
<p><img src="https://michelenasti.com/images/typescript-cover-image.jpg" alt="" /></p>
<p>Once you install Typescript, default values are a bit dumb. For example, 99% of the world would put the source code in a <code>src</code> directory, and typescript has to be configured explicitly this way. Let's see my <code>tsconfig.json</code>:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"compilerOptions"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"module"</span><span class="token operator">:</span> <span class="token string">"esnext"</span><span class="token punctuation">,</span><br /> <span class="token property">"target"</span><span class="token operator">:</span> <span class="token string">"esnext"</span><span class="token punctuation">,</span><br /> <span class="token property">"sourceMap"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token property">"outDir"</span><span class="token operator">:</span> <span class="token string">"./build/"</span><span class="token punctuation">,</span><br /> <span class="token property">"moduleResolution"</span><span class="token operator">:</span> <span class="token string">"node"</span><span class="token punctuation">,</span><br /> <span class="token property">"strict"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token property">"lib"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token string">"dom"</span><span class="token punctuation">,</span><br /> <span class="token string">"esnext"</span><span class="token punctuation">,</span><br /> <span class="token string">"dom.iterable"</span><span class="token punctuation">,</span><br /> <span class="token string">"scripthost"</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"include"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token string">"src/**/*.ts"</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"compileOnSave"</span><span class="token operator">:</span> <span class="token boolean">true</span><br /><span class="token punctuation">}</span></code></pre>
<p>This is part of my setup. I will go through some of these options so you can choose wisely.</p>
<h3>The "target" option</h3>
<p>Typescript is a compiler from a superset to a subset of Javascript features and <code>target</code> determines what kind of EcmaScript (Javascript's formal name) should be targeted.</p>
<p>Possible values are:</p>
<ul>
<li><code>ES3</code> (default)</li>
<li><code>ES5</code></li>
<li><code>ES6</code></li>
<li><code>esnext</code></li>
</ul>
<p>If you leave it blank, or choose <code>ES3</code>, your code would be compatible with very old browsers, like Internet Explorer 5 or Netscape Navigator (joking, but not that much). ES3 is the first standardized version of Javascript.</p>
<p>You may think this is a great default value and be tempted to use <code>fetch</code>, or <code>Promise</code>, and typescript will let you use them, BUT <strong>typescript will not add any polyfill to your code.</strong> Beware of this!</p>
<p>Similarly you can choose to run several different options, <code>ES5</code> (if your users are on Internet Explorer), <code>ES6</code>... I prefer <code>esnext</code> to be able to use the latest features of the language and leave to Babel the transpilation step.</p>
<p>Did you know that prior to ES6 there was no official way to import some JS code inside another JS code? The community came out with several options and the next section will discuss them.</p>
<h3>The "module" option</h3>
<p><strong>module</strong> determines the way modules (and files) express dependencies between each other. Possible values are:</p>
<ul>
<li><code>None</code></li>
<li><code>CommonJS</code></li>
<li><code>AMD</code></li>
<li><code>System</code></li>
<li><code>UMD</code></li>
<li><code>ES6</code></li>
<li><code>ES2015</code></li>
<li><code>ESNext</code></li>
</ul>
<p>Ouch, so many options for a single task: importing code. Why?! That's because the evolution of Javascript is non-linear, and many of them do not apply for every platform.</p>
<p>If you use <strong>None</strong> you cannot use <code>import</code>, <code>export</code>, <code>require</code>... nothing.</p>
<p><strong>AMD</strong>, <strong>System</strong> and <strong>CommonJS</strong> are (IMHO) a thing of the past. the first (AMD) came from <a href="https://requirejs.org/docs/whyamd.html" title="RequireJS">RequireJS</a> and the second from <a href="https://github.com/systemjs/systemjs" title="SystemJS">SystemJS</a>. These two are browser's solutions. Not so long ago there was no way to express dependencies across JS files, so many competing systems came out to solve the same problem. AMD and SystemJS did not get enough fortune to become a standard.</p>
<p><strong>CommonJS</strong> is a spec that is very similar to what NodeJS has implemented (<code>require</code>/<code>exports</code>).</p>
<p><strong>UMD</strong> stands for <em>Universal Module Definition</em> and tries to deliver a module that works with every possible module definition defined before.</p>
<p>However, the ECMAScript community went for the <code>import/export</code> solution, and node has embraced that too, and we have that available in <code>ES6</code> and <code>ES2015</code>.</p>
<p>Again, I prefer to use Typescript as a superset of JS, so I choose <code>esnext</code> and let webpack decide how to bundle stuff together.</p>
<h3>outDir or outFile ?</h3>
<p>You have decided which module system you want to use (you know my vote for <code>ES6</code>), but do you want a single file in output (<code>outFile</code>) or many single files in a output directory (<code>outDir</code>)?</p>
<p>If you write JS for the browser you may be tempted to go for <code>outFile</code> and let TS deal with everything. You may think that this way you can skip webpack, babel and so on. The problem is that TS allows to use this option only with <code>amd</code> and <code>system</code>. So this is a hard requirement for that.</p>
<p>The second big problem is that, to build a single file with all modules, Typescript needs also to know the <em>order</em> of files, so <em>you</em> have to provide the list in the right order. Imagine having 30-50-100 modules...</p>
<p>Again, my advice is to use<code>outDir</code>. For every source file you get a compiled file in the output directory. Then, again, did I tell you of webpack + babel?</p>
<h3>ModuleResolution</h3>
<p>This setting accepts two possible values: <code>node</code> and <code>classic</code>. At this point there's no need to have a value different than <code>node</code> here. For what I read <a href="https://www.typescriptlang.org/docs/handbook/module-resolution.html">in the official documentation</a>,</p>
<blockquote>
<p>Nowadays, this strategy [<code>classic</code>] is mainly present for backward compatibility.</p>
</blockquote>
<h3>Lib</h3>
<p>Since we are working in the browser and we are targeting a JS version greater than ES2015, I use the defaults that are <code>DOM,ES6,DOM.Iterable,ScriptHost</code>. The list of all available libraries is <a href="https://www.typescriptlang.org/docs/handbook/compiler-options.html">here</a>. If you forget to put those, Typescript will output weird errors like missing <code>Set</code> interface or other stuff - I guess the problem is that TS goes in <code>node_modules</code> and finds code that is obviously thought for Node, and it fails somehow.</p>
<h3>compileOnSave</h3>
<p>An option for IDEs to trigger compilation on a file save. If your IDE does not support it you can disable it but having it turned on it does not harm.</p>
<h3>strict: true</h3>
<p>You use Typescript because you want it to catch syntax errors in your library. However, the default value for this option is <code>false</code> and this means that many syntax checks are not done for you.</p>
<p>If you start a new project, I strongly advise to turn on this option. If you, like me, are converting a JS library to TS, this option may transform your job in hell: introduce it gradually and slowly.</p>
<h2>Conclusions</h2>
<p>So many choices to do and I guess inexperienced developers do not know what to do at first. Hoping to do them a favour!</p>
<p>I've also cited <strong>Webpack + Babel</strong> setup here, and this will be discussed in another article. It will require some changes here and there, and typescript will only be used to check the syntax of the project, but the transpilation will be done by Babel.</p>
<p>Curious? Stay tuned!</p>
Typescript, Babel, Webpack, ESLint: my configuration
2019-06-27T00:00:00Z
https://michelenasti.com/2019/06/27/typescript-babel-webpack-eslint-my-configuration.html
<p>The purpose of this post is to give you, dear reader, an example of the configuration I use on some projects. Configuring the whole stack is sometimes frustrating. Here's what works for me.</p>
<h2>Typescript</h2>
<p>I <a href="https://michelenasti.com/2019/06/24/typescript-why-so-complicated.html">just wrote an article</a> about that and you should read it. It contains my <code>tsconfig.json</code> and why every option is configured that way.</p>
<h2>Babel ( 7.x )</h2>
<blockquote>
<p><strong>Update (2019-10-16)</strong>: on my work projects I had to use Typescript for transpiling, I'll cover this in the next paragraph. You can skip Babel setup if you do not need this.</p>
</blockquote>
<p>The fun fact is, even if we use Typescript and our files end with <code>.ts</code>, to build the project we will <em>skip</em> typescript. Let me be clearer. We will use <strong>Babel</strong>, that is a transpiler, and <strong>Webpack</strong>, that is a bundler. Webpack will bundle files that are transpiled by Babel.</p>
<p>But wait?! Babel transpiles what? The latest trend is that Babel will <em>strip out</em> every Typescript interface, annotation or type inference and will just transform the file to a normal <code>.js</code> file. This output will then be given in input to Webpack.</p>
<p>Enough talking! Time to install babel and it's dependencies:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/preset-typescript</code></pre>
<p>Here's my babel setup:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//babel.config.js</span><br /> module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token parameter">api</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> isTest <span class="token operator">=</span> api<span class="token punctuation">.</span><span class="token function">env</span><span class="token punctuation">(</span><span class="token string">'test'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">const</span> targets <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">browsers</span><span class="token operator">:</span> <span class="token string">"> 0.25%, not dead"</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>isTest<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">delete</span> targets<span class="token punctuation">.</span>browsers<span class="token punctuation">;</span><br /> targets<span class="token punctuation">.</span>node <span class="token operator">=</span> <span class="token string">"current"</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"presets"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">[</span><br /> <span class="token string">"@babel/env"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token string-property property">"useBuiltIns"</span><span class="token operator">:</span> <span class="token string">"entry"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"corejs"</span><span class="token operator">:</span> <span class="token string">"3.0.0"</span><span class="token punctuation">,</span><br /> targets<br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token string">"@babel/typescript"</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"plugins"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token string">"@babel/proposal-class-properties"</span><span class="token punctuation">,</span><br /> <span class="token string">"@babel/proposal-object-rest-spread"</span><br /> <span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Quick explanation:</p>
<ul>
<li>I use Jest and puppeteer to test my code, and Jest requires <code>targets.node</code> to be set to <code>current</code>. If you don't use Jest and Puppeteer, just remove the <code>isTest</code>check.</li>
<li>Babel works with presets, each preset will transform some pieces of code depending on some rules.</li>
<li><code>@babel/env</code> is a quasi-default rule, it will transform our js code to something that is understandable by something that is parsable by what's defined in <code>targets</code>.</li>
<li><code>@babel/typescript</code> is the king here. It will <em>strip off</em> all Typescript specific data (interfaces, types, etc.).</li>
<li>the two plugins added are already in a very advanced standardization process, but they are added because typescript allows them.</li>
</ul>
<h3>Is it working?</h3>
<p>run this command:</p>
<pre class="language-bash"><code class="language-bash">npx babel src --out-dir build_ts <span class="token parameter variable">--extensions</span> <span class="token string">".ts,.tsx"</span> --source-maps inline</code></pre>
<p>Here we'll run babel on the directory <code>src</code>, the output will go in <code>build_ts</code><em>.</em></p>
<p>It works? without hassles? Let's go to the next step:</p>
<h2>Alternative to Babel: Typescript compiler (UPDATE)</h2>
<p>When I wrote this article I was just fine with babel + typescript + webpack, unfortunately shit happens and in this case the shit's name is called Internet Explorer.</p>
<p>Babel is notoriously good at adding polyfills for older browsers, however this process was not particularly suitable for my use case since I develop a library and I did not want to bloat my project with external dependencies, only to support IE.</p>
<p>So, if you use Babel you can add polyfills and you can follow <a href="https://www.codementor.io/lawwantsin/babel-polyfills-transforms-presets-csbnjsok6">this article</a> to do it. <strong>I still think Babel is the best solution for webapps (not for libraries)</strong>. Babel does also do a great job in converting ES6-only objects (like Promises) to ES5 with the right configuration. With the right set of babel plugins and polyfills, you can convert whatever syntax from the past, the present and the future.</p>
<p>Instead, <strong>Typescript will deliver a smaller bundle but you must be sure to be using a syntax that will work with IE</strong>. If you use Promises, or fetch for example, TS will not provide any polyfill for it and you must manually check that IE will not break. Not the best experience, but not an impossible job either.</p>
<p>In our case, the "broken" error came from the use of <code>Symbol</code>, that is used in <code>for... of</code> iterations. Read more to see what to change if you want to support <code>tsc</code> instead of babel for transpiling code.</p>
<h2>Webpack (4.x)</h2>
<p>Installation:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">npm</span> i --save-dev webpack webpack-cli babel-loader</code></pre>
<p><strong>For Typescript:</strong> change <code>babel-loader</code> with <code>ts-loader</code>.</p>
<p>Here's my configuration:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//webpack.config.js</span><br /><br /><span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'path'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> webpack <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'webpack'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">entry</span><span class="token operator">:</span> <span class="token string">'./src/index'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">'development'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">devtool</span><span class="token operator">:</span> <span class="token string">'false'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">path</span><span class="token operator">:</span> path<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'build'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">filename</span><span class="token operator">:</span> <span class="token string">'dist.js'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">resolve</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">extensions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'.ts'</span><span class="token punctuation">,</span> <span class="token string">'.tsx'</span><span class="token punctuation">,</span> <span class="token string">'.js'</span><span class="token punctuation">,</span> <span class="token string">'.json'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">module</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">rules</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token comment">// Include ts, tsx, js, and jsx files.</span><br /> <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.(ts|js)x?$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><br /> <span class="token literal-property property">exclude</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><br /> <span class="token literal-property property">loader</span><span class="token operator">:</span> <span class="token string">'babel-loader'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>The most important part is the <code>rules</code> part. Our rule is that every file will be tested, if it ends with <code>.ts</code> it will be passed to plugin <code>babel-loader</code> that will pass the file to <code>babel</code> before assembling it with babel.</p>
<p><strong>For Typescript</strong>: change the <code>rules: [ ...</code> section with</p>
<pre class="language-js"><code class="language-js"><span class="token literal-property property">rules</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token comment">// Include ts, tsx, js, and jsx files.</span><br /> <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\.(ts|js)x?$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><br /> <span class="token literal-property property">exclude</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">node_modules</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><br /> <span class="token literal-property property">loader</span><span class="token operator">:</span> <span class="token string">'ts-loader'</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span></code></pre>
<blockquote>
<p><strong>Note</strong>: This is the most basic webpack setup I could come up with. Reality is much more complicated than this and <a href="https://webpack.js.org/configuration/output/">you may need to set additional options/plugins</a>. Don't be afraid to check out webpack documentation, you'll learn interesting things</p>
</blockquote>
<h3>Is it working?</h3>
<pre class="language-bash"><code class="language-bash">$ npx webpack</code></pre>
<p>It should create the file <code>build/dist.js</code>.</p>
<h2>ESLint</h2>
<p>Installation:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> i --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin</code></pre>
<p>Configuration code:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//.eslintrc.js</span><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">parser</span><span class="token operator">:</span> <span class="token string">'@typescript-eslint/parser'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">parserOptions</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">ecmaVersion</span><span class="token operator">:</span> <span class="token number">2018</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">sourceType</span><span class="token operator">:</span> <span class="token string">'module'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'@typescript-eslint'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token string-property property">'env'</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">'browser'</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token string-property property">'es6'</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token string-property property">'jest'</span><span class="token operator">:</span> trueparser<span class="token operator">:</span> <span class="token string">'@typescript-eslint/parser'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">parserOptions</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">ecmaVersion</span><span class="token operator">:</span> <span class="token number">2018</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">sourceType</span><span class="token operator">:</span> <span class="token string">'module'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'@typescript-eslint'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token string-property property">'extends'</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token string">'eslint:recommended'</span><span class="token punctuation">,</span><br /> <span class="token string">'plugin:@typescript-eslint/recommended'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token string-property property">'rules'</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">//...</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<ul>
<li>we tell ESLint to use the <a href="https://www.npmjs.com/package/@typescript-eslint/parser">typescript parser</a> with some specific parser options.</li>
<li>Then we add the plugin @typescript-eslint that will provide us with some useful rules for typescript...</li>
<li>...Like <code>@typescript-eslint/recommended</code>, that contains a few rules that are recommended by the community.</li>
</ul>
<blockquote>
<p><strong>Note</strong>: if you convert a legacy Javascript project to typescript, the <code>typescript-eslint/recommended</code> set of rules is ... <em>too much</em>. That's why I usually comment it out and try to fix problems day after day.</p>
</blockquote>
<h2>JEST</h2>
<p>this article doesn't really cover Jest and Typescript, but I'll only add this quick snippet for those who may find it useful. To configure Jest with TS:</p>
<p>install <code>ts-jest</code>:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> i --save-dev ts-jest</code></pre>
<p>add these lines to <code>jest.config.js</code>:</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">'^.+\\.tsx?$'</span><span class="token operator">:</span> <span class="token string">'ts-jest'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span></code></pre>
<h2>Ready to start your projects?</h2>
<p>Hope my configuration files have helped you creating your new projects. Can't wait to know what you're working at!</p>
Detect network issues from your pc to a remote url
2019-08-02T00:00:00Z
https://michelenasti.com/2019/08/02/detect-network-issues-to-urls.html
<p>Suppose you see that a website, or a URL, that seems to be unaccessible.</p>
<p>The first thing to do is to check <strong>if the website is down just for you or for the whole universe</strong>. There are many websites that do this, the one I discovered recently is <a href="https://www.uptrends.com/tools/uptime">Uptrends</a>.</p>
<p>By putting a url in uptrends you can see how long a resource will take to load from a bunch of locations in the world:</p>
<p><img src="https://files.slack.com/files-pri/T04DE1CDJ-FLXGT5B8Q/image.png" alt="" /></p>
<p><em>Oh no! There's an issue in Europe!</em></p>
<p>Another check you can do is for <strong><em>proper hackers</em></strong> <em>®</em><strong>.</strong> Launch this command from bash:</p>
<p>$ sudo mtr -rwc 50 <url></p>
<p>The result is a very nice list of all hops made by data from our PC to the remote host</p>
<pre class="language-bash"><code class="language-bash">➜ ~ <span class="token function">sudo</span> <span class="token function">mtr</span> <span class="token parameter variable">-rwc</span> <span class="token number">50</span> michelenasti.com <br /><span class="token punctuation">[</span>sudo<span class="token punctuation">]</span> password di musikele: <br />Start: <span class="token number">2019</span>-08-02T10:27:39+0200<br />HOST: musikele-XPS-15-9560 Loss% Snt Last Avg Best Wrst StDev<br /> <span class="token number">1</span>.<span class="token operator">|</span>-- _gateway <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">2.7</span> <span class="token number">2.2</span> <span class="token number">1.9</span> <span class="token number">4.2</span> <span class="token number">0.4</span><br /> <span class="token number">2</span>.<span class="token operator">|</span>-- ??? <span class="token number">100.0</span> <span class="token number">50</span> <span class="token number">0.0</span> <span class="token number">0.0</span> <span class="token number">0.0</span> <span class="token number">0.0</span> <span class="token number">0.0</span><br /> <span class="token number">3</span>.<span class="token operator">|</span>-- <span class="token number">172.18</span>.18.108 <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">10.6</span> <span class="token number">10.7</span> <span class="token number">8.5</span> <span class="token number">16.8</span> <span class="token number">1.5</span><br /> <span class="token number">4</span>.<span class="token operator">|</span>-- <span class="token number">172.18</span>.19.226 <span class="token number">56.0</span>% <span class="token number">50</span> <span class="token number">11.7</span> <span class="token number">11.3</span> <span class="token number">9.0</span> <span class="token number">30.4</span> <span class="token number">4.6</span><br /> <span class="token number">5</span>.<span class="token operator">|</span>-- <span class="token number">172.19</span>.177.38 <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">16.5</span> <span class="token number">16.9</span> <span class="token number">15.6</span> <span class="token number">19.6</span> <span class="token number">0.7</span><br /> <span class="token number">6</span>.<span class="token operator">|</span>-- <span class="token number">172.19</span>.177.2 <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">27.4</span> <span class="token number">27.8</span> <span class="token number">24.6</span> <span class="token number">69.4</span> <span class="token number">8.6</span><br /> <span class="token number">7</span>.<span class="token operator">|</span>-- etrunk49.milano50.mil.seabone.net <span class="token number">52.0</span>% <span class="token number">50</span> <span class="token number">28.2</span> <span class="token number">28.3</span> <span class="token number">26.6</span> <span class="token number">40.6</span> <span class="token number">2.9</span><br /> <span class="token number">8</span>.<span class="token operator">|</span>-- et9-1-0.ashburn2.ash.seabone.net <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">122.1</span> <span class="token number">121.0</span> <span class="token number">119.1</span> <span class="token number">129.5</span> <span class="token number">2.0</span><br /> <span class="token number">9</span>.<span class="token operator">|</span>-- cloudflare.ashburn2.ash.seabone.net <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">128.3</span> <span class="token number">133.4</span> <span class="token number">125.5</span> <span class="token number">216.4</span> <span class="token number">14.6</span><br /> <span class="token number">10</span>.<span class="token operator">|</span>-- <span class="token number">104.28</span>.13.252 <span class="token number">0.0</span>% <span class="token number">50</span> <span class="token number">124.4</span> <span class="token number">124.8</span> <span class="token number">123.5</span> <span class="token number">126.3</span> <span class="token number">0.6</span><br />➜ ~ </code></pre>
<p><code>mtr</code> stands for <strong>My TraceRoute</strong> and combines ping with traceroute. It gives a lot of information about the routers crossed during the path to the destination.</p>
<p>One may think that there are some errors in the upper report, however you should have in mind that many routers are configured to delete ICMP packets (the ones used by mtr, or by ping) or to slow them down for quality assurance.</p>
<p>I really suggest you to read <a href="https://www.linode.com/docs/networking/diagnostics/diagnosing-network-issues-with-mtr/">this article from Linode</a> that explains almost everything you may want to know and it's very easy to follow.</p>
<p>Enjoy your networking!</p>
What is the difference between var, let and const in Javascript
2019-08-17T00:00:00Z
https://michelenasti.com/2019/08/17/what-is-the-difference-between-var-let-and-const-in-javascript.html
<p>I did some interviews in the last few days, the role we were searching was "super-duper senior javascript master" so I asked this very simple question as starter:</p>
<blockquote>
<p>What is the difference between <code>var</code>, <code>let</code> and <code>const</code> to declare a variable?</p>
</blockquote>
<p>Surprisingly, a lot of js developers do not know the right answer.</p>
<p><img src="https://michelenasti.com/images/proxy.duckduckgo.com-3.jpeg" alt="" /></p>
<h2>Var: the first, the oldest, the deprecated</h2>
<p>Var is with us since the very first version of Javascript. All major browsers, including Internet Explorer, still and will understand a var declaration forever. So why I say it's deprecated?</p>
<p>Well, when you declare a variable with var, it's declaration is <em>hoisted</em> and the assignment will happen in a separate time. This is unnoticed for most of the time, unless you hit a bug and start complaining. Let's see an example:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// ...looks fine, isn't it?</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>someVariable<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"will this execute?"</span><span class="token punctuation">)</span> <br /> <span class="token comment">// will throw ReferenceError: someVariable is not defined</span><br /><span class="token punctuation">}</span></code></pre>
<p>Now refresh the page and run this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> someVariable<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"will this execute? and what is the value of someVariable?"</span><span class="token punctuation">,</span> someVariable<span class="token punctuation">)</span> <br /> <span class="token comment">// apart from console.logs, we only added this line: </span><br /> <span class="token keyword">var</span> someVariable <span class="token operator">=</span> <span class="token boolean">true</span> <br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Value of someVariable again: "</span><span class="token punctuation">,</span> someVariable<span class="token punctuation">)</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token comment">//output:</span><br /><span class="token comment">// "will this execute? and what is the value of someVariable? undefined"</span><br /><span class="token comment">// "Value of someVariable again: true"</span></code></pre>
<p>Did you notice that I declared the variable inside the if block? You would expect an error ("dude, your variable does not exist at this moment") instead Javascript will just let you do it. Why?</p>
<h3>Hoisting</h3>
<p>Javascript will read your .js file twice. The first time it will <strong>move on the top of the current unction</strong> all <code>var</code> declarations, initializing these variables with <code>undefined</code>.</p>
<p>The second time it reads the <code>.js</code> file, it will now execute the interpreted code.</p>
<p>Based on this, the previous code becomes something like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// </span><br /><span class="token keyword">function</span> <span class="token function">someFunction</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> someVariable <span class="token operator">=</span> <span class="token keyword">undefined</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>someVariable<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"will this execute? and what is the value of someVariable?"</span><span class="token punctuation">,</span> someVariable<span class="token punctuation">)</span> <br /> someVariable <span class="token operator">=</span> <span class="token boolean">true</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Value of someVariable again: "</span><span class="token punctuation">,</span> someVariable<span class="token punctuation">)</span> <br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<blockquote>
<p>Note: If the script is executed in a "global" context, you should imagine a "wrapper function" around your code. So variable declarations are moved on top of the file.</p>
</blockquote>
<p>Since this <em>movement of variables</em> happen in functions, we can say tha <code>var</code> is function-scoped. This code should not surprise you anymore:</p>
<pre class="language-javascript"><code class="language-javascript"> <span class="token keyword">if</span> <span class="token punctuation">(</span>someCondition<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> someCondition <span class="token operator">=</span> <span class="token boolean">false</span><br /><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> someCondition <span class="token operator">=</span> <span class="token boolean">true</span><br /><span class="token punctuation">}</span></code></pre>
<p>... But when you check the code and see that <code>someCondition</code> is true, but the first if block didn't execute, you'll end in wtf.</p>
<h3>So, what's the problem</h3>
<p>The biggest problem is that this way of thinking of variables is completely different from all other programming languages. <strong>Developers coming from other programming languages will believe that a</strong> <code>var</code> <strong>is block scoped, while indeed it's function scoped</strong>. This will take you to weird bugs.</p>
<p>That's why the JS community decided to fix this with ES6 introducing two new variable declarations: <strong>let</strong> and <strong>const</strong>.</p>
<h2>Let</h2>
<p>Imagine a world where you declare a variable and it will behave exactly as you thought: it's block scoped, it doesn't do strange movements, and the browser will throw at you the right errors if you forget to initialize. Let me present you ... <code>let</code> !</p>
<p>The same code as before will now be more reasonable:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> someVariable<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"will this execute? and what is the value of someVariable?"</span><span class="token punctuation">,</span> someVariable<span class="token punctuation">)</span> <br /> <span class="token keyword">let</span> someVariable <span class="token operator">=</span> <span class="token boolean">true</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Value of someVariable again: "</span><span class="token punctuation">,</span> someVariable<span class="token punctuation">)</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// : ReferenceError: someVariable is not defined</span></code></pre>
<p>Once the <code>var</code> problem is fixed, what if you want to declare an immutable reference to a variable? In the past, doing so in Javascript was weird and included accessing some private properties; now that's a lot easier.</p>
<h2>Const</h2>
<p>With const, we can finally declare constant <em>references</em>, but what does it mean? Let's see an example again:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> <span class="token constant">PRICE</span> <span class="token operator">=</span> <span class="token number">33</span><br /><span class="token keyword">const</span> <span class="token constant">TITLE</span> <span class="token operator">=</span> <span class="token string">'wonderful title'</span><br /><span class="token keyword">const</span> anObject <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">a</span><span class="token operator">:</span> <span class="token string">'obj property'</span> <span class="token punctuation">}</span> <br /><br /><span class="token constant">PRICE</span><span class="token operator">=</span><span class="token number">44</span> <span class="token comment">// TypeError: invalid assignment to const `PRICE'</span><br /><span class="token constant">TITLE</span> <span class="token operator">=</span> <span class="token string">'less beautiful title'</span> <span class="token comment">// TypeError: invalid assignment to const `TITLE'</span><br /><br />anObject <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// TypeError: invalid assignment to const `anObject'</span><br />anObject<span class="token punctuation">.</span>a <span class="token operator">=</span> <span class="token string">'new obj property'</span> <span class="token comment">// no error! </span></code></pre>
<p><em>The last two lines should bring you special attention.</em> <code>const</code> will <strong>freeze the reference</strong>. If the referenced object holds other properties, you can still change them.</p>
<h2>Conclusions</h2>
<p>Since Internet Explorer is fading away, the days we can finally get rid of <code>var</code> are very close. The problem is that until it is not completely dead we have to transpile our code with <code>let</code> and <code>const</code> to <code>var</code>.</p>
<p>A lot of developers have no idea of why we transpile and what is happening under the hood, and it's a pity because it's my first question when I interview JS senior developers :/</p>
Jest and Puppeteer from the barricades
2019-10-02T00:00:00Z
https://michelenasti.com/2019/10/02/jest-and-puppeteer-from-the-barricades.html
<p>When I started my career as a software developer I immediately become "friend" with the concepts of <strong>unit testing and integration testing</strong>. At the time (2012) the backend was written in Java and the frontend was written in GWT, a Java framework to build UIs that is compiled to JS.</p>
<p>Unfortunately <strong>all our testing efforts were only directed towards the backend</strong>, probably because of the language; this means that <strong>we could not find UI bugs before it was too late</strong>.</p>
<p>Fast fotward many years, GWT is not a thing anymore (good for you!), and Javascript is one of the most studied and used languages of the world. Javascript ecosystem is growing exponentially with thousands of packages, frameworks, ideas, applications that come out all the time. The language itself is evolving every year, after the first 20 years of no interesting additions. So... testing in JS became a necessity!</p>
<p>At the start of 2019 there was a lot of hype around Javascript testing, mainly because of:</p>
<ul>
<li>popular UI frameworks (react, vue, angular...) were designed from the ground up to be testable;</li>
<li>NodeJS has become stable and mature</li>
<li>The ubiquity of the web (progressive web apps, or React Native-like projects, made possible to use js on mobile too)</li>
</ul>
<p>I also read an interesting article saying (cannot find it anymore) "<strong>the js community has finally realized that web apps must be E2E tested first, instead of unit tested</strong><em>"</em>. That's true because e2e tests have become easier (but not dead easy) to write and to repeat. Let's see it!</p>
<p><img src="https://michelenasti.com/images/1BM_RS2-DjYk_JZUKr4uq0g.png" alt="" /></p>
<h2>How to structure the code</h2>
<p>Unit tests and End-to-end (e2e) tests are executed in two different environments: unit tests are executed in a browser-like environment provided by jest, while e2e tests are executed in a real browser. They need different configurations, that's why I've created two directories:</p>
<ul>
<li><code>unit</code> for unit tests</li>
<li><code>e2e</code> for end to end tests</li>
</ul>
<p><a href="https://github.com/musikele/jest-puppeteer-tutorial">All the code is here</a>.</p>
<h2>Unit testing: JEST</h2>
<p>Let me introduce Jest. <strong>Jest</strong> is a testing framework mainly maintained by Facebook. It has a lot of features:</p>
<ul>
<li>support for typescript,</li>
<li>babel,</li>
<li>webpack,</li>
<li>latest ES features,</li>
<li>support for testing timers, classes, etc.</li>
<li>support for mocking</li>
<li>a clear-enough documentation</li>
</ul>
<p>To install:</p>
<p>$ npm i --save-dev jest
$ jest --init # create an initial conf file</p>
<p>and, for the purpose of using ES6 syntax:</p>
<pre class="language-shell"><code class="language-shell"><span class="token function">npm</span> i --save-dev babel-jest @babel/core @babel/preset-env</code></pre>
<p>Then, let's add some Babel configuration to transpile code:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//babel.config.js</span><br />module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">presets</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">[</span><br /> <span class="token string">'@babel/preset-env'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">targets</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">browsers</span><span class="token operator">:</span> <span class="token string">'>2%'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<h2>Let's run our first test</h2>
<p>This example is slightly modified from JEST <a href="https://jestjs.io/docs/en/getting-started">getting started</a>. Suppose we have this function:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example01/sum.js</span><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> a <span class="token operator">!==</span> <span class="token string">'number'</span> <span class="token operator">||</span> <span class="token keyword">typeof</span> b <span class="token operator">!==</span> <span class="token string">'number'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'The sum function accepts only numbers'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">return</span> a <span class="token operator">+</span> b<br /><span class="token punctuation">}</span></code></pre>
<p>Ok! Let's write a test for this function:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example01/sum.spec.js</span><br /><span class="token keyword">import</span> sum <span class="token keyword">from</span> <span class="token string">'./sum'</span><br /><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'sum module'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should sum two numbers'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token function">sum</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<blockquote>
<p><strong>Try it!</strong> Use <code>$ npx jest</code> from the command line and see the output! Jest will search for *<em>.test.js and *.spec.js</em> files, and will execute them.</p>
</blockquote>
<p><code>describe()</code> is used to define a test suite - a logical group of tests that are related. You can write tests directly at the top level, or you can wrap as many <code>describe</code> blocks you want one into another; these names hare helpful when the tests start failing because they provide some context of what went nuts.</p>
<blockquote>
<p><strong>Suggestion</strong>: I prefer to write at least one containing the module name and optionally another that will explain the base condition I am checking. Example:</p>
</blockquote>
<pre class="language-javascript"><code class="language-javascript"><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Products service'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'The product is not in the database'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Let's go back to our example. the function <code>it()</code> (or <code>test()</code>) is the actual test function that will be executed. Usually our tests are comprised of three parts:</p>
<ul>
<li><strong>setup</strong> - import the code, insert the configuration, etc.</li>
<li><strong>execution</strong> - where we actually call the sum function</li>
<li><strong>expectations test</strong> - where we check that the test has actually succeded.</li>
</ul>
<p>We use Jest function <code>expect()</code> to assert that the result of the function is <code>5</code>; thanks to a bunch of <a href="https://jestjs.io/docs/en/expect">Jest expectations</a>, we can test pretty much everything is testable.</p>
<blockquote>
<p><strong>Suggestion</strong>: limit yourself to test only the public API of a module. There's no need to test the inner workings!</p>
</blockquote>
<p>And now let's write a test for the <em>bad</em> case:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example01/sum.spec.js</span><br /><span class="token keyword">import</span> sum <span class="token keyword">from</span> <span class="token string">'./sum'</span><br /><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'sum module'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should sum two numbers'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should explode with exception'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">try</span> <span class="token punctuation">{</span><br /> <span class="token function">sum</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token string">'pippo'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span> <span class="token keyword">catch</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBeDefined</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<blockquote>
<p><strong>Hint</strong>: try to make your tests fail. Otherwise you can never know if they're really working.</p>
</blockquote>
<h3>Can we check DOM modifications?</h3>
<p>Jest integrates JSDOM, a browser DOM API implementation in pure JS. In my experience it will work very similarly to a real browser, and I found it super useful. For example, let's write a function that will change toggle a class over a DOM element:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example02/toggleClass.js</span><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">(</span><span class="token parameter">domElement<span class="token punctuation">,</span> className</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> domElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>This very super stupid snippet will add or remove to a <code>domElement</code> a <code>¢lassName</code> .</p>
<p>Now let's write a Jest test for it:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example02/toggleClass.spec.js</span><br /><span class="token keyword">import</span> toggleClass <span class="token keyword">from</span> <span class="token string">'./toggleClass'</span><span class="token punctuation">;</span><br /><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Toggle class on DOM element'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'attach an element to the Document and toggle a clas on it'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// (1) </span><br /> <span class="token keyword">const</span> div <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><br /> div<span class="token punctuation">.</span>id <span class="token operator">=</span> <span class="token string">'myDiv'</span><br /> document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>div<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <br /> <span class="token comment">// (2) div actually attached to the virtual dom:</span><br /> <span class="token keyword">const</span> sameDivAsBefore <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'myDiv'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>div<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span>sameDivAsBefore<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <br /> <span class="token comment">// (3)</span><br /> <span class="token function">toggleClass</span><span class="token punctuation">(</span>sameDivAsBefore<span class="token punctuation">,</span> <span class="token string">'myClass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>sameDivAsBefore<span class="token punctuation">.</span>classList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span><span class="token string">'myClass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <br /> <span class="token comment">// (4) </span><br /> <span class="token function">toggleClass</span><span class="token punctuation">(</span>sameDivAsBefore<span class="token punctuation">,</span> <span class="token string">'myClass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>sameDivAsBefore<span class="token punctuation">.</span>classList<span class="token punctuation">)</span><span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span><span class="token string">'myClass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Heeeey, a 3 lines function tested by a 20 lines test :D Well, lot of the code was added to show you something.</p>
<p>At (1) I create a DIV and attach to a Document. <strong>The Document object is provided by JSDOM and is instantiated by Jest</strong> - if you are using NodeJS, remember to change a setting in jest configuration and you're done.</p>
<p>At (2) I just show that <em>all the DOM API is available</em>, so I retreive the same element with <code>getElementById()</code>.</p>
<p>Then, I will add a class to this element by calling <code>togleClass()</code>. We then check that the class has been added by using <code>expect().toContain()</code>.</p>
<p>Finally, we call again <code>toggleClass</code> (4) and check that the dom element does <strong>not</strong> contain the class anymore.</p>
<p>This is so <strong>powerful</strong>, that by enabling some features (via configuration) I could also <em>load scripts</em> (like jquery) and use it in the test! Not bad.</p>
<h2>Can we mock other functions/classes?</h2>
<p>Yes! That's how you reach the zen of unit test.</p>
<p>Let's imagine we have a class <code>apiClient</code> that will ask another class <code>apiService</code> to make a REST call. <strong>We want to test apiClient</strong>.</p>
<p>In this example, <code>apiService</code> will ask for currency exchange from Euro to many other currencies. <code>apiClient</code> will in turn transform the data in a way it is usable, by just keeping the <code>USD</code> value.</p>
<p>Here's the code for <code>apiService.js</code>, the module that will call the REST endpoint:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example03/apiService.js </span><br /><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getCurrencyExchange</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> window<span class="token punctuation">.</span><span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">' https://api.ratesapi.io/api/latest'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> json <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token keyword">return</span> json<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>And the consumer, <code>apiClient.js</code> (<strong>this</strong> <em>is the module we want to test</em>):</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example03/apiClient.js</span><br /><span class="token keyword">import</span> <span class="token punctuation">{</span> getCurrencyExchange <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./apiService'</span><br /><br /><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getEuroDollar</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> currencyExchange <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getCurrencyExchange</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">1 Euro is </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>currencyExchange<span class="token punctuation">.</span>rates<span class="token punctuation">.</span><span class="token constant">USD</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> dollars</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br /> <span class="token keyword">return</span> currencyExchange<span class="token punctuation">.</span>rates<span class="token punctuation">.</span><span class="token constant">USD</span><br /><span class="token punctuation">}</span></code></pre>
<p>What's the problem with this module?</p>
<ul>
<li>the value of the USD exchange rate will change over time</li>
<li>if the network is down, the test will fail</li>
</ul>
<p>So we need... <strong>mocks!</strong></p>
<p>Here's a unit test that will mock out <code>apiService</code> from using the network.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// unit/example03/apiClient.spec.js</span><br /><br /><span class="token comment">//class to test (1)</span><br /><span class="token keyword">import</span> <span class="token punctuation">{</span> getEuroDollar <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./apiClient'</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// stuff we need to mock (2)</span><br /><span class="token keyword">import</span> <span class="token punctuation">{</span> getCurrencyExchange <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./apiService'</span><br />jest<span class="token punctuation">.</span><span class="token function">mock</span><span class="token punctuation">(</span><span class="token string">'./apiService'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'apiClient tests'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">//reset all mocks before use! (3) </span><br /> <span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> getCurrencyExchange<span class="token punctuation">.</span><span class="token function">mockClear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should return euro/dollar exchange'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">//when called, return this result (4) </span><br /> getCurrencyExchange<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">rates</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token constant">USD</span><span class="token operator">:</span> <span class="token number">2000</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token comment">//call the function under test </span><br /> <span class="token keyword">const</span> euroDollarChange <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getEuroDollar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <br /> <span class="token comment">//expect that getCurrencyExchange has been called (5)</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>getCurrencyExchange<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toHaveBeenCalled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>euroDollarChange<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">2000</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<ul>
<li>(1) - we import the function we want to test</li>
<li>(2) - but we need to intercept and change the behaviour of <code>getCurrencyExchange()</code> so we also import it, and with <code>jest.mock()</code> we tell jest we want to modify the behaviour</li>
<li>(3) - imagine we have not just one test like in this example, but a suite of tests, with this code we tell Jest to clear eventual mocks between tests.</li>
<li>(4) - here we are <em>really</em> <strong>mocking the behaviour</strong> of <code>getCurrencyExchange</code>. Jest will return the value specified in <code>mockResolvedValue</code> instead of making the actual network call. In this mock, inflation was really a thing and 1 Euro corresponds to 2000 $ (Euro rulez!).</li>
<li>After having instructed the test, we check that the mocked function has really ben called and the mocked value has been returned.</li>
</ul>
<p>I only scratched the surface of what is possible to do with Jest, and there are thousand of mock tecniques depending of what you want to mock: classes, timers, async methods.. jest has you covered.</p>
<p>Now let's talk about... Puppeteer.</p>
<h2>Puppetteer: a headless Chrome</h2>
<p><a href="https://pptr.dev/">Puppeteer</a> is a node library that allows to control a Chromium instance. Let's quiclky see an example. To install:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> i --save-dev puppeteer </code></pre>
<p>Puppeteer will download a beta-version of chromium that will be used to run our tests. Usually the puppeteer version uses a chromium version that is 2-3 versions further than the regular chrome, and this is done to test new chromium too.</p>
<p>Let's try to run a super-easy example that comes from their website: let's take a screenshot of my personal website, michelenasti.com:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> puppeteer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'puppeteer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> browser <span class="token operator">=</span> <span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">newPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token string">'https://michelenasti.com'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">screenshot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token string">'example.png'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">await</span> browser<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Some things to note:</p>
<ul>
<li>Most of the time we will interact with the <code>page</code> object, almost all useful stuff is there</li>
<li>Almost all methods are async, so we have to use <code>await</code> everywhere</li>
<li>Chromium is started with a very dumb resolution, 800x600, like my first 486 monitor.</li>
<li>The documentation is your friend: check it out!</li>
<li>If you want to <em>see</em> chrome in action, pass this configuration to <code>puppeteer.launch()</code>:</li>
</ul>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">await</span> puppeteer<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">headless</span><span class="token operator">:</span> <span class="token boolean">false</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Nothing more to say, we are ready to glue together jest + puppeteer in our tests!</p>
<h2>jest-puppeteer to the rescue!</h2>
<p>Let's instal <a href="https://github.com/smooth-code/jest-puppeteer">jest-puppeteer</a>, a jest plugin to remote-control puppeteer in jest:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">npm</span> <span class="token function">install</span> --save-dev jest-puppeteer</code></pre>
<p>At this point, in the root of your project, create an <code>e2e</code> folder that will contain all your e2e tests. Copy <code>jest.config.js</code> from <code>unit</code> and add this option:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token punctuation">{</span><br /> <span class="token string-property property">"preset"</span><span class="token operator">:</span> <span class="token string">"jest-puppeteer"</span><br /><span class="token punctuation">}</span></code></pre>
<p>This setting will tell Jest to launch tests in a different environment, that is Chrome.</p>
<p>Ready to run our first test?</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// e2e/example01/01.spec.js</span><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'Google'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">beforeAll</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token string">'https://google.com'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should display "google" text on page'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">await</span> <span class="token function">expect</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toMatch</span><span class="token punctuation">(</span><span class="token string">'google'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>I cheated, this example comes directly from jest guide, but it is helpful to understand how it works.</p>
<ul>
<li>in the <code>beforeAll</code> block we navigate to the google page;</li>
<li>in the test block, we check that <code>google</code> is somewhere on the page.</li>
</ul>
<blockquote>
<p><strong>Suggestion</strong>: try to change website with michelenasti.com ... it will fail</p>
</blockquote>
<p>Let's move to something more interactive. We will borrow <a href="http://todomvc.com/examples/vue/">Todolist MVC app</a> from <a href="http://todomvc.com/">this website</a> (they replicate the same app in every possible framework so you can study them).</p>
<p>The idea is to:</p>
<ul>
<li>add a todo item</li>
<li>check that it has been added to the list</li>
<li>remove it</li>
</ul>
<p>Let's write such test!</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// e2e/example02/01.spec.js</span><br /><span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'TodoApp MVC tests'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">goto</span><span class="token punctuation">(</span><span class="token string">'http://todomvc.com/examples/vue/'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><br /> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'should add a todo'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// (1)</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">type</span><span class="token punctuation">(</span><span class="token string">'input.new-todo'</span><span class="token punctuation">,</span> <span class="token string">'this is a todo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span>keyboard<span class="token punctuation">.</span><span class="token function">press</span><span class="token punctuation">(</span><span class="token string">'Enter'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> label <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'div.view label'</span><span class="token punctuation">)</span><br /> <span class="token comment">// (2)</span><br /> <span class="token keyword">const</span> property <span class="token operator">=</span> <span class="token keyword">await</span> label<span class="token punctuation">.</span><span class="token function">getProperty</span><span class="token punctuation">(</span><span class="token string">'innerHTML'</span><span class="token punctuation">)</span><br /> <span class="token keyword">const</span> jsonValue <span class="token operator">=</span> <span class="token keyword">await</span> property<span class="token punctuation">.</span><span class="token function">jsonValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>jsonValue<span class="token punctuation">)</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>jsonValue<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token string">'this is a todo'</span><span class="token punctuation">)</span><br /> <span class="token comment">// (3)</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">hover</span><span class="token punctuation">(</span><span class="token string">'div.view label'</span><span class="token punctuation">)</span><br /> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">click</span><span class="token punctuation">(</span><span class="token string">'div.view button.destroy'</span><span class="token punctuation">)</span><br /> <span class="token keyword">const</span> label2 <span class="token operator">=</span> <span class="token keyword">await</span> page<span class="token punctuation">.</span><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'div.view label'</span><span class="token punctuation">)</span><br /> <span class="token function">expect</span><span class="token punctuation">(</span>label2<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>I don't want to illude you, you'll learn many tricks with experience, like me while I was preparing these examples.</p>
<ul>
<li>at (1) we write in the big input box our todo item</li>
<li>at (2) we check that the value has been added to the list</li>
<li>at (3) we first hover the item, in order to make the destroy button appear; then we click it. <em>What happens if we do not hover over the text?</em></li>
</ul>
<h2>Conclusions</h2>
<p>...And that's it! Testing is an art and as such must be constantly exercised. These frameworks are complex because they allow to do complex things. During this talk I tried to give you hints of what you should do based on my experience.</p>
<p>I also want to highlight some <strong>alternatives</strong> of these frameworks. For <strong>Jest</strong> the most famous alternative is <strong>Mocha</strong>, it has almost the same API. However, to use mocha you have to install a lot of external libraries (the assertion library, the mocking framework, etc), so I prefer jest because it already contains everything.</p>
<p>Regarding <strong>puppeteer</strong>, there are some old competitors like <strong>Selenium</strong> (and you can write tests in other languages too, or you can also use other browsers); if you want to stick with Chrome, <strong>Cypress</strong> is another great tool that is worth to try. It is much more visual than puppeteer.</p>
<p>Remember:</p>
<blockquote>
<p>If there is no test written, than you didn't test it.</p>
</blockquote>
How internet ads work
2019-10-21T00:00:00Z
https://michelenasti.com/2019/10/21/how-internet-ads-work.html
<p>Since money was invented, people felt the urge to advertise their products to others. I live very near to Pompeii (Italy) and if you visit this incredible town you'll discover that in 79 a.D. there were a lot of ads painted on walls, trying to sell you wine, prostitutes, or ask for a vote.</p>
<p>Fast-forward a couple of millenia, we have many websites that are free to their users - think at your favourite newspaper. This is totally different from how newspapers made money few years ago: you had to buy them in shops. So, how they pay salaries to their employees now?</p>
<p>You can guess - since it's the topic of this post - many websites earn money by displaying ads. So here's an engineering point of view of this world.</p>
<p><img src="https://michelenasti.com/images/pexels-photo-802024.jpeg" alt="" /></p>
<blockquote>
<p>This year I started working for an adtech company and this post tries to describe what I've learned of this mega-curious world.</p>
</blockquote>
<h2>How were ads in 1999?</h2>
<p>In 1999 I was 14 years old, so what I know is based on the tales of wise old men. When a news company launched a website, it had to find <strong>advertisers</strong> that wanted to display ads on their website. The news company, in adtech words, is called <strong>publisher</strong>. Many publishers had sales teams that contacted advertisers directly, trying to make a deal like "Publisher will display 1,5 million impressions of these <em>images</em> (in adtech terms: <strong>creatives</strong>) and Advertiser will pay 100k bucks".</p>
<p>Once the deal was closed, there were needs to:</p>
<ul>
<li><strong>track impressions</strong> - how many times ads were rendered? and how many times they were actually <em>viewed</em>? and clicked?</li>
<li><strong>handle multiple campaigns concurrently</strong> - I can close a deal with advertiser A for 1.5mln impressions per 100k, but then close another deal with advertiser B for 500k impressions for 200k - which one would you show most? and with what algorithm?</li>
</ul>
<p>Turns out, you need a software to implement these logics. A software like that is called <strong>Ad Manager</strong>. One of the most famous ad managers is...</p>
<h2>Doubleclick for Publishers</h2>
<p><strong>DoubleClick for Publishers,</strong> also known as <strong>DFP</strong>, has been one of the most successful ad managers ever created. I think part of his success was due to the fact it was a SaaS based product, developed when SaaS was not even a word :)</p>
<p>DFP allowed to:</p>
<ul>
<li><strong>organize campaigns, creatives, orders</strong>, <strong>line items</strong>, etc. Many obscure terms I will not explain here. I will only explain what is needed to understand the rest of this article, btw.</li>
<li><strong>serve the</strong> <strong>most valuable creative</strong> amongst the closed deals</li>
<li><strong>track, analyze, report</strong> - because after all, based on this data, advertisers will pay publishers!</li>
</ul>
<p>Not all publishers had the resources for a <em>dedicated sales team</em> or the power to <em>engage with the most valuable customers</em>. Also, when campaigns are exhausted, there are no remnant ads to show and this can lead to blanks in some moments of the year. But things are going to change again because at some point in 2007...</p>
<h2>Google acquires DFP</h2>
<p>With the acquisition of DFP, Google does not change the original philosophy of DFP as an Ad Manager, but introduces a new element: instead of choosing ads from deals only, publishers can also include ads from <strong>AdExchange</strong> (abbreviated: <strong>AdEx</strong>). This means that when for an <em>ad unit</em> (the area to fill on the publisher's website) there are no ads to display, DFP could deliver an ad from Google's network of advertisers. These are good news for small publishers who could get ads from AdEx and do not base their incomes on direct sales only.</p>
<p>With Google acquisition, DFP also started to track users and display ads based on their navigation history. So if you navigate an exclusive watch website, you'll see the ad for that watch following you on other websites.</p>
<p>DFP has now been renamed in <strong>Google Ads Manager (GAM).</strong> I will use GAM for the rest of the article but the world is still calling DFP at the time of writing.</p>
<p><img src="https://michelenasti.com/images/google-ad-manager.png" alt="" title="Google Ads Manager when you setup a new account, appears like this" /></p>
<h2>What happens when a browser asks for an ad to GAM</h2>
<p>Let's pretend you're a publisher. To integrate GAM on your website you must <em>create an account and configure it</em>. I am not an expert in this field so I will not discuss this in detail. One important part, however, is that <strong>when you setup a "deal", you can insert the advertiser's creatives in the form of javascript code</strong>, and <strong>if this creative is selected for display, it will be injected in the user page inside an iframe</strong>. Keep this in mind: it is one of the foundation pieces of header bidding.</p>
<p>Let me introduce another important piece of terminology now, a "deal" is called <strong>Order</strong> in GAM words.</p>
<p>The next step is to integrate <code>googletag</code> code. This is a JS library that will make the request to GAM for an ad, that will be shown in a <code>div</code> on your website. Along with the request, <code>googletag</code> will pass some cookies trying to identify the user and only show ads that match the user interests.</p>
<p><code>googletag</code> will take care of <strong>resizing and render the ad, track viewability</strong> (that means: has the ad been in the viewport for more than 1.5 seconds? if yes, publisher will be paid), and detect fraud. The internals are obscure, probably to avoid malicious attempts to reverse engineer and forge attacks.</p>
<p>An important aspect of <code>googletag</code> is that <em>you can define custom key-value pairs along with the request</em>, and this feature is called (in GAM terms) <strong>Targeting</strong>. A targeting example could be <code>country=IT</code> or <code>interests=sports</code>. GAM can be later configured to read these key-value pairs, and do some logic on them - like, if the user is from Italy and is interested in sports, show him ads from the important sports brand I have a deal with.</p>
<p>Why I said that it's important? well, <strong><em>Targeting</em> is another key concept of Header Bidding. I'll explain later on.</strong></p>
<p><a href="https://support.google.com/admanager/answer/1638622?hl=en">Here is an example googletag call</a>.</p>
<h2>Is there life after Google?</h2>
<p>In Ad tech terms, Google is a <strong>SSP</strong> - <strong>Supply Side Partner</strong> - meaning it will supply publishers with ads (and pay them). Are there other SSPs ? Yes there are, I guess more than 2-300, however the number of ads they can provide is much lower than that of Google and Facebook. An estimation is that 60% of all ads are traded by Google and Facebook, leaving the remaining SSPs trading the remaining 40%. So it's a tough market and many websites do not even think of substituting or integrating with other SSPs.</p>
<p>However, living with Google only is not the best option. Google does not make any effort to be transparent on how much ads have been payed, so <strong>some suppose that Google is showing us the most profitable ads for <em>them</em></strong> - not for <em>publishers</em>.</p>
<p>Due to the fact that Google has the majority of the market, it is <em>ignoring</em> all other SSPs and does not offer an easy way to integrate them in DFP.</p>
<p><strong>SSPs still have some advantages</strong>. For example, when asking for an ad to an SSP, we know exactly how much we're going to be paid if we render that ad, and this means <strong>we can start auctions amongst all configured SSPs and get the best priced ad.</strong> The price is usually given in <strong>CPM</strong> (<em>cost per mille -</em> cost per thousand impressions), so if an ad has a cpm of 10$, one single impression is worth 0.01$ ! (The average cpm, at the time of writing, is around 1$ if you were asking).</p>
<p>Interestingly, <strong>we can use this feature to deduce the price of GAM ads</strong>, too. How? Let's introduce...</p>
<h2>Header Bidding</h2>
<p>the name <em>Header bidding (HB)</em> comes from the fact that, historically, what I'm going to describe was done in the <code><head></code> part of the page. This is no more true, but the name remained :)</p>
<p><strong>HB <em>hacks</em> GAM to allow other SSPs to bid for the same ad units</strong>. Let's recap the three foundational items:</p>
<ul>
<li>we can inject whatever creative we want for custom orders.</li>
<li>we can define targeting on GAM requests, so we can display specific ads to some users.</li>
<li>SSPs will tell us what they're paying us, and we can use this piece of information to estimate the price of GAM ads too.</li>
</ul>
<p>You're finally ready to discover <strong>the HB process</strong>, <em>for dummies:</em></p>
<ol>
<li><strong>We start an auction for every ad unit on page</strong>. This means that we ask a bunch of SSPs (except Google, at this stage!) how much they are willing to pay if their ad is chosen.</li>
<li>SSPs will answer with their responses, and <strong>we choose the SSP with the highest value</strong>. SSPs will also answer with the ad to render, we will store it in memory and render in the next stage.</li>
<li><strong>We send to GAM a request by setting some targeting keys</strong>: among them, we set the <strong>SSP name</strong> that has the best offer (for reporting) and the <strong>price</strong> of the ad.</li>
<li>When GAM receives the request, it will decide what to do according to a specified set of rules. <strong>if AdEx has an ad that has an higher value, it will return AdEx ad. Otherwise, it will return the creative that has been specified by us.</strong></li>
<li>If the returned creative is the piece of javascript we defined, this means that the SSP won against GAM too, and the creative will contain only one instruction: <em>render the ad that has won the auction at step 2.</em></li>
</ol>
<p>How can we infer the price of a GAM ad ? By GAM terms of service, GAM is obliged to send us an ad that costs at least 0.01 cpm than the one we sent at step 3. So if the SSP ad was priced 0.26, we know GAM ad is worth at least 0.27 .</p>
<h2>Prebid.js</h2>
<p>What you've just read here is nothing more than the logic behind <a href="http://prebid.org/index.html">PrebidJS</a>, an <strong>open source project born as a synergy between the SSPs community</strong>.</p>
<p><img src="https://michelenasti.com/images/Schermata-da-2019-10-19-11-08-37.png" alt="" title="Prebid.org homepage" /></p>
<p>Prebid will perform the auction amongst the publisher chosen set of SSPs, then will query GAM, and finally will render the ad. Prebid also contains code to display video ads, mobile ads, handle different currencies, and much more.</p>
<p>Here you can see an example of <a href="http://prebid.org/dev-docs/getting-started.html">prebid in action</a>.</p>
<h2>That's it?</h2>
<p>That's definitely <strong>not</strong> all, folks! I've just <em>scratched the surface</em> of the ad tech world; I apologize if something is not clear (ask in the comments!) and probably I've generalized too much.</p>
<p>If you're a programmer, you'll be able to understand a lot of concepts that you'll encounter in the future. I've deliberately decided not to talk about many GAM concepts that are not worth introduce at this point, but <strong>you're highly encouraged to study if you want to succeed in this field</strong>. GAM alone has a big slice of market share of all ad tech servers and you'll for sure end up working on it, sooner or later ;)</p>
Tips & Tricks from my linux experience
2019-10-27T00:00:00Z
https://michelenasti.com/2019/10/27/tips-tricks-from-my-linux-experience.html
<blockquote>
<p>This article is the summary of a talk I gave for <a href="https://www.facebook.com/events/686761111828223/permalink/686775338493467/">Linux Day Avellino 2019</a>. Slides are <a href="https://show.zohopublic.com/publish/h93fsd5b97c46dd57465e9769d542c3871bea">here</a>.</p>
</blockquote>
<p>While at university, I - like many other students - were encouraged to install linux on our laptops, to learn the fundamentals of programming and the internals of operating systems.</p>
<p>I guess it was 2006-2007 and I installed Ubuntu in dual boot on my machine, but since I'm not an easy guy <strong>I decided to use it as my first operating system</strong>, leaving Windows as a backup. Blog posts, magazines and books agreed, linux was <em>ready</em> for desktop PCs.</p>
<p>Could not agree: <strong>at the time, drivers support was a mess</strong>. There was no support, for example, for a usb DSL modem and a printer I owned. I remember buying an external wifi adapter and that was not working too. I had to consume a lot of time on forums, even translating from french, to make my laptop working.</p>
<p>However, I was happy with what I had, also because I do not play videogames and I really do not need Windows for any special reason (no adobe, no MS Office, etc.). Also, 100% of my studies could be done from linux standard tools, and this was something I really loved.</p>
<p>Let's fast-forward a couple of years, I bought a Mac with my first salary - a Mac that lasted 5 years - and a Windows PC - this one. I used it with WSL (<em>bash on Windows</em>) for some time, then I had to switch back to linux as my daily operating system because WSL is <em>so slow</em>.</p>
<p>In this article I'll share some neat tricks I found useful, together with some stories about my experience. If you want to reach the buddha developer status, there are some steps to make!</p>
<h2>Get comfy with the command line</h2>
<p>If you're a Windows user, you can avoid command line for the rest of your life, even as a developer. But if you work in a Unix environment, the terminal becomes the best tool for your job. <strong>Knowing bash is really important</strong>: you can automate almost any aspect of your daily tasks. The problem is that <strong>bash has the most obscure syntax ever</strong>, and honestly I can't even remember how to check for a file existence. what a mess!</p>
<p>However, Bash is still super popular, and if you administer or work on a remote server, chances are your only tool is bash. Here are some resources:</p>
<ul>
<li><a href="https://github.com/dylanaraps/pure-bash-bible">Pure Bash Bible</a></li>
<li><a href="https://coderwall.com/p/oqtj8w/the-single-most-useful-thing-in-bash">The single most useful thing in bash</a></li>
<li><a href="https://devhints.io/bash">Bash cheatsheet</a></li>
<li>... and obviously Stack Overflow, Google, etc.</li>
<li>don't forget to learn <em>vi</em> or <em>emacs</em> (i prefer the former).</li>
</ul>
<h2>How to backup a Linux system?</h2>
<p>When I switched to MacOS everybody was talking about how <em>advanced</em>, <em>superior</em>, <em>cool</em> was Time Machine, the backup solution every Mac has. It is actually very nice to use, mainly because the graphics seem to come from the future, but I did not like one aspect: the backup files were compressed (and encrypted) in a way that only another Mac could understand.</p>
<p>I am not a vendor lock-in fan, so I did not want to be obliged to buy Macs forever only because of my backups, so I started looking around at alternatives. There are many, either free or payed, but there is one sitting there in your <code>/usr/bin</code> called <strong>rsync</strong>.</p>
<p><strong>Rsync</strong> is a Unix tool to synchronize the content of two folders that can be located over the net, or on different hard disks. It is capable of everything you can think of - preserving attributes, navigating symlinks, encrypt files, incremental backups, etc. - and has a very nice man page.</p>
<p>So I decided to build <a href="https://github.com/musikele/backupscript">my own backup script</a>. It was not an easy journey. It's written in bash, it will take your data from your home directory to a network position using SSH. It will also copy only differences in files, and hard linking unchanged files, so everytime I run it I get a snapshot of my pc that takes the smallest possible space.</p>
<p>If you can access a network position with ssh access (without password), you can schedule a cron job to run every X hours and have a full backup of your computer.</p>
<p>Nice, isn't it? However, I ended up not using my script.</p>
<p>A backup is a critical process and must run flawlessly without hassles, without the user even knowing it's running. In my case, since my solution was "homemade", I had to check every now and then if it was working, and what was going on, etc.</p>
<p>After a couple of years of honoured service I moved to <a href="https://www.duplicati.com/">Duplicati</a>, an open source .NET project that has a lot of features for backup nerds like me.</p>
<p>So next question: where I am storing all the data?</p>
<h2>buy (or build) a NAS</h2>
<p><strong>The problem</strong>: decouple my life from Dropbox, Google Drive, etc.</p>
<p>I have more than 100+ GB of data, going back to 2003, that I want to preserve forever. Also, I want to backup my photos and my entire PC, everyday, twice per day. Buying space from one of those accounts can cost up to 100€ per year.</p>
<p>But I'm a nerd and I want to do it myself!</p>
<p>What I am looking for is a <strong>NAS</strong> (<em>Network Attached Storage</em>), basically a Server connected to the internet. After having looked around, building one means you have time, knowledge and a spare computer. I did not have time and a spare computer so I decided to buy one :)</p>
<p>I bought 5 years ago a NAS with two 3TB hard disks, total expense 320€, duplicated in a RAID 1 configuration. This means that the two disks are exact clones and when a disk will fail I can substitute it with a new one and the system will autoclone the disks.</p>
<p>But buying a NAS it's not only great because of the power you get, but because you can experiment with a lot of cool features! That's a list of what I accomplished:</p>
<ul>
<li>buy a domain and point to the NAS - access the NAS from everywhere</li>
<li>use let's encrypt to generate certificates and connect to the NAS with HTTPS only</li>
<li>use 2 factor auth</li>
<li>install apps on it, like owncloud (drobox clone)...</li>
<li>a webserver with mysql and a wordpress instance (for my wedding)...</li>
<li>a mail server for my custom domain ...</li>
<li>a DLNA for video streaming</li>
<li>SSH access with no password</li>
<li>SAMBA, SFTP, etc for network devices,</li>
<li>an italian proxy to watch football matches from abroad (go Napoli!)</li>
<li>... and whatever else you may ever need!</li>
</ul>
<p>I did not expect to learn that much when I first bought my NAS, but yes, after looking at what I've gained 5 years after, I really did a good choice.</p>
<h2>For your pet projects, buy a private server</h2>
<p>I have a bunch of pet projects and I run all of them on a virtual machine I've bought online.</p>
<p>The cool thing is that this machine costs me nothing - around 2.5€ per month - and I spend my time to setup the machine, but that time is rewarded with experience.</p>
<p>In the past I used to configure everything with command line and by following the <a href="https://help.ubuntu.com/lts/serverguide/">Ubuntu Server Guide</a>, today I don't do this anymore (too time consuming and error-prone) and I usually install <a href="https://www.virtualmin.com/">Virtualmin</a> and then try to figure out my needs with it.</p>
<p>With Virtualmin you can pretty much administer your machine as you want, like your NAS, but I prefer to physically separate the two things because if a hacker ever gets access to this machine (that is designed to be exposed to the web) I don't want to loose all my personal life.</p>
<p>So my setup is to create a third level domain (XXX.abc.def), then create an https certificate for it thanks to lets encrypt, set up a cron job that will take care of renewing the certificate, and then setup database etc.</p>
<p>In the past I used heroku but now i find that platform too limiting if my requirements are slightly more complex.</p>
<h2>And now... some cool terminal tricks</h2>
<p>For my terminal I use <a href="https://ohmyz.sh/">Oh-My-Zsh</a>, it will make your command line more beautiful and with tons of plugins you can configure autocomplete for stuff that usually is not auto-completable - like git, or docker, etc.</p>
<p>When I have to test the communication between a client and a server I use <a href="https://ngrok.com/">ngrok</a>, that will create an address like <code><random_numbers>.ngrok.io</code> even with HTTPS, all of this for free. Then, you can inspect what happened during the connection. The cool thing is that you can use this tool to show to your colleagues some work, even if you're not on the same net.</p>
<p>I wrote a nice article about <a href="https://michelenasti.com/2019/04/03/ssh-cheatsheet-from-zero-to-hero.html">SSH, from zero to hero</a> and you shoul read it - it's the <strong>basic foundation</strong> for all the stuff you read above.</p>
<h2>Some notes on Dell XPS 15"</h2>
<p>Installing Ubuntu on my Dell was not so flawless as I thought. Google will be your friend (or enemy) if you try this path. Here are some insights:</p>
<ul>
<li>
<p>since the monitor is 4K, the native terminal is too small to be read.</p>
</li>
<li>
<p>Connecting a non-external monitor that is not-4k is a real problem. I generally launch this command to connect a monitor on the right:</p>
<p>$ xrandr --output HDMI1 --scale 2x2 --mode 1920x1080 --fb 3840x0 --pos 3840x0</p>
</li>
</ul>
<p>(trick: connect the cable, and when the system looks freezed, run this command. I don't know why, but if I wait for the system to unfreeze and then run the command, the system will definitely block!)</p>
<ul>
<li><strong>Wayland</strong> - an alternative composer to Xorg - is not ready for 4K monitors. Gnome apps will look fine, but Electron apps will not.</li>
<li>This PC also has a <strong>touchscreen</strong>. It works nice with Chrome, but it is useless with Firefox because it will select text instead of scroll. <a href="https://superuser.com/questions/1151161/enable-touch-scrolling-in-firefox">Here are some fixes</a>.</li>
<li><strong>Snap or apt</strong> ? I tend to embrace new stuff and I like snap because applications get updated faster, since they're self-contained. Also, many command line tools can be snap-installed. However, I also got some 4K problems with snap apps so I had to install some apps few times before finding one that works without issues.</li>
</ul>
<p>That's all folks! I hope you enjoy linux as much as I did.</p>
Thinking of customers like a CEO: lessons learned along the way
2019-11-21T00:00:00Z
https://michelenasti.com/2019/11/21/thinking-about-your-customer-like-a-ceo.html
<p><em>The story I'm going to talk about is not related to my current job, anyway.</em></p>
<p>Some time ago I jumped on a brand new job, a <em>service oriented</em> one, where we had many customers and we offered a <strong>product as a service</strong>.</p>
<p>One of the first tasks I was assigned to was to check what was going on with a customer: he claimed there was a misbehaviour in our product.</p>
<p>Replicating this kind of problems was difficult, because at the time the <strong>product itself was not very mature</strong>; also, many customers were <strong>using the product in a very unconventional way</strong> and this caused more trouble; finally, the product was <strong>very susceptible about network conditions, users, browsers, locations</strong>, etc. A real mess, but that's probably the same in every other startup :)</p>
<p>Anyway, I tried hard to reproduce the bug as described in the ticket, but whatever I did, I failed. So I took the easy path, and wrote as a response <em>sorry but I am unable to replicate the problem, probably the issue was temporary and has resolved by itself</em>.</p>
<p>After no longer than 1-2 hours, the CEO answered to my card. And wrote:</p>
<blockquote>
<p>this is not the way we should ever answer to our customers; <strong>this is the best way to see them go to competitors</strong>. We need to find an answer that is supported by proofs, or fix the problem.</p>
</blockquote>
<p>I don't remember, however, what was the issue in the end. I asked a more experienced colleague if he could help me and in a couple of hours we figured out. But I think that day I leaned some important lessons.</p>
<p><img src="https://michelenasti.com/images/customer.jpg" alt="Not me, not my coworkers" title="A couple of programmers" /></p>
<p><em>about the headerImg: i work remotely, so this is not me and not my coworker, but it can be seen as a beautiful depiction of us discussing of problems :)</em></p>
<h2>Lessons learned</h2>
<ul>
<li>In every company (and more importantly in a startup) <strong>customers are the most valuable asset</strong>. "<em>This is the best way to see customers go to competitors</em>" - I think this will stick in my head for a long time.<br />
Try to open a complain to Google, or Facebook. Your experience may tell that it's very rare that you'll have to deal with their customer care, given that their products are almost bug free from day zero. However, shit happens and if it happens to you, you'll discover that reaching out to some human and talk about your problem can be impossible.</li>
<li>Not really in target with the title of the post, but ... <strong>I used to feel ashamed if I had to ask for the help of a senior colleague, but now I think that was the right thing to do.</strong> Honestly, we (as programmers - debuggers) should not spend more than 2-3 hours on a problem, or without hypothesis to test. I pretend to be smart and to solve every possible mistery in code, but in reality I sometimes focus on details while the problem is in the bigger picture, or the inverse. Asking help to somebody with experience is not like saying "<em>I give up</em>" or "<em>I'm not good enough for this job</em>". It's more like "<em>I need another point of view to reason on this problem</em>".</li>
</ul>
<p>Hope you find these lessons useful.</p>
Pair Programming vs Pull Requests
2019-12-09T00:00:00Z
https://michelenasti.com/2019/12/09/merge-requests-vs-pair-programming.html
<p>I attended a hackaton recently: an italian bank organizes an event every year for employees only, me and another programmer friend were invited by an employee to conribute to his idea.</p>
<p>Once we had defined the problem we wanted to solve, and defined who-does-what, we also decided that there was some space to actually <em>implement</em> the solution. While other team members worked on slides, number, charts, motivation, me and my friend did produce a web application that could be shown to the jury.</p>
<p>What happened during the 24 hours we worked on? <strong>We <em>pair-programmed</em>.</strong> Two people, one pc only.</p>
<h2>What happens when you do pair programming</h2>
<p>This was unexpected, but I felt <em>good</em>.</p>
<ul>
<li>When you code by yourself from zero you have to make a lof of choices, like what framework to use and why, how to store information, how to design the app, etc. <strong>Having another person crosscheck the ideas is great</strong>: many bugs were catched before even writing the first line of code.</li>
<li>Having another pair of eyes on the monitor helped <strong>reduce other kind of bugs (like typos)</strong> before hitting <em>Save</em>.</li>
<li><strong>After a couple of hours of work we both had an incredible amount of knowledge of the project</strong>; at some point we decided it was time to split efforts, and he worked on backend while I set up domain, hosting etc. We clearly knew what we wanted and how independent tasks fitted into the whole picture.</li>
</ul>
<p>So, should we all switch to pair programming and <em>trash</em> a computer every two developers? No. Pair programming (or <em>pair thinking</em>) is great to :</p>
<ul>
<li><strong>teach stuff to a new hire</strong> while you work on it</li>
<li><strong>Design a project or a feature</strong> in its initial phase</li>
<li><strong>discover</strong>, before it's too late, <strong>hidden or conflicting use cases</strong>.</li>
<li><strong>increase the quality of the project</strong> (fewer bugs)</li>
<li><strong>spread knowledge</strong> of the project <strong>to all team members</strong>.</li>
</ul>
<p>And the downsides?</p>
<ul>
<li>Probably <strong>managers fear</strong> one thing: <strong>that productivity is halfed</strong>. Two programmers doing the work of one? no way.</li>
<li>Also, <strong>not all tasks are great to be worked on by two people</strong>, like writing tests, or bug fixing.</li>
<li>Finally, if your <strong>coworker</strong> is a <strong>stubborn</strong> or a <strong>dickhead</strong>, this may be not a great developer experience.</li>
</ul>
<p>In conclusion, <strong>my experience has been great</strong>, I will probably do it again if necessary and I'd love to know any stories (either success or horror) about pair programming. The comment section is there for you!</p>
<h2>And what about Merge (or Pull) Requests?</h2>
<p>Since internet was born we saw the rise of thousands of programmers working distributedly to enhance complex projects. To enable this, new instruments were born too. Git and Github for example popularized a way to contribute to other repositories, called <strong>Pull Request</strong>, that basically consists in:</p>
<ul>
<li>"fork" (copy) a project under your account</li>
<li>do your work on your copy</li>
<li>push a Pull Request to the original project.</li>
<li>Somebody from the oroginal project will review the modifications, and if everything is allright, the modifications are merged.</li>
</ul>
<p>This process is totally asynchronous and it's the one I currently use at work, since my team is remote. At my job, the policy is that at least two people must review and approve the code before merging with the original project.</p>
<p>What are the advantages of this approach?</p>
<ul>
<li>Obviously, it <strong>works great for distributed teams</strong>.</li>
<li><strong>The history of the project is preserved</strong>, thanks to version control.</li>
<li>Linus Torvalds says that <em>the more eyes see your code, the less bugs arrive to the users</em>.</li>
</ul>
<p>On the other side:</p>
<ul>
<li>Imagine you're working on a feature that touches several entities and business rules, what if you forget to cover one hidden path? You'll only discover when somebody will review it, and if you modification is heavy, this may mean <strong>you have to rewrite a lot of code</strong>.</li>
<li><strong>Understanding a codebase</strong> that is not yours, or that is written in a language that you're not fluent with, <strong>can be daunting</strong>.</li>
<li>Even small tasks may require <strong>a lot of time</strong> (yours and that of your reviewers) to figure out if the merge request is fine.</li>
</ul>
<h2>Is there space for a new programming model?</h2>
<p>Pair programming is great for new hires, or when a new functionality has to be designed, etc.</p>
<p>Obviously pair programming is much easier when two people are in the same room, looking at the same monitor, but <strong>in 2019 there is plenty of tools that can help remote workers</strong> to achieve the same experience.</p>
<p>Pull Requests do their job, but I feel like the sum of two brains is much greater than two individual brains working alone, so <strong>we should</strong> - as an industry - <strong>find a way to merge the two things in a new process that may enhance productivity and quality of code</strong>.</p>
<p>In a PR-only world, we leave the <em>knowledge sharing</em> to documentation, that is obviously important, but we're left to the willing of the programmer to properly document his choices, design points, etc. What if the programmer is not a great writer? Or lazy? Or did not have time to write it?</p>
a review of the book The Phoenix Project
2020-01-30T00:00:00Z
https://michelenasti.com/2020/01/30/a-review-of-the-book-the-phoenix-project.html
<p>I've just completed the reading of a book calld <strong>The Phoenix Project - a Novel about IT, DevOps, and helping your business win.</strong></p>
<p><iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-eu.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=ilblodimicnas-21&o=29&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=1942788290&linkId=38b7816128820e3be07e2099a6bf8c57"></iframe></p>
<p>The book in question is the story of <strong>Bill</strong>, a guy who was managing a legacy systems team, and all of a sudden has found himself being nominated <em>Vice President of IT Operations</em>.</p>
<p>During the first day of this new role, infrastructure broke and a critical payroll system started returning all zeros. A nice start!</p>
<p>It's funny how Bill discovers that there's no version control, nobody knows what happened, what everyone is working on, etc - it's like waking up in the middle of a ocean, and you can only navigate by sight.</p>
<p>During the whole book we'll meet several roles, like <strong>Wes</strong> and <strong>Patty</strong> (two managers who work in Operations), <strong>Brent</strong> (an IT Engineer that seems to be the only one to know what's going on in every single moment), <strong>Steve</strong> (the CEO), <strong>John</strong> (the Security Officier) and <strong>Erik</strong> (the guru).</p>
<p>The book, expecially in the first part, is fantastic. You'll see that, no matter what they do, things continue to randomly break! Just like reality.</p>
<p>If you, like me, started working in IT during 2010, you probably remember the pain of releasing software in production. The root cause is that there's a profund mismatch in skills from people who work in IT operation (those who are specialized in configuring <em>machines</em>) and those who work in development.</p>
<p>Luckily, DevOps (and Agile) has come to the rescue. Now many companies do or can do <a href="https://www.youtube.com/watch?v=LdOe18KhtT4">10 deploys per day</a>. Isn't it great?</p>
Microsoft Surface GO: more than a tablet
2020-02-06T00:00:00Z
https://michelenasti.com/2020/02/06/microsoft-surface-go-more-than-a-tablet.html
<p>I've just received a new corporate PC, and my old computer (<a href="https://michelenasti.com/2017/10/03/recensione-dell-xps-15-2017.html">a Dell XPS, 15", 16 GB of RAM</a>) was sitting at home, unused. So I decided to sell it and to buy a convertible - a tablet with a keyboard.</p>
<p>I wrote down the list of things I needed and, based on that, the best choice was the <strong>Microsoft Surface Go</strong>. Why have I preferred this to ipads or android tablets?</p>
<h3>I'm a developer</h3>
<p>So my first constraint is that I need to run some tools that are speciific to my work, like Visual Studio Code (an IDE to develop programs) and much more (NodeJS, Python, ...) I'm not going to do actual work on it, but for sure I'd love to follow tutorials and practice with side projects. The idea of locking in a platform that didn't allow me to do that was appalling.</p>
<h3>I'm a blogger</h3>
<p>Not a professional blogger, of course, but I love writing my blog and I want to do it also from home. Surprisingly, at home I rarely turn on the pc to write something, it looks an overkill. So yes, I needed something with a good keyboard.</p>
<h3>I'm a traveller</h3>
<p>When I'm on vacation I don't want to carry over a big computer, but just something to organize my trip and to be connected to the word. Mobile phone is fine most of the time, but have you ever tried to crosscheck trains/planes/guides/museums? Sometimes a bigger screen is just better.</p>
<h2>Microsoft Surface Go</h2>
<p>So why the <strong>Microsoft Surface Go</strong>? This tablet is probably conceived for students but it's performing incredibly well as a day-to-day pc. I'll try to outline two lists, what I like and what I don't like, so you can get an idea of pros and cons.</p>
<p><img src="https://michelenasti.com/images/microsoft_surface_go.jpg" alt="" /></p>
<h3>What I like</h3>
<ul>
<li>The keyboard. Seriously, it's super.</li>
<li>I can fix the keyboard's layout according to my preferences. In fact, I configured my italian keyboard to write characters ` and <code>~</code>. Try to do that on Andoid or Apple...</li>
<li>The fact that I can install Visual Studio Code, NodeJS, and even Bash On Linux.</li>
<li>Many apps are not natively available but using the newer Microsoft Edge I could install many of them as html5 apps.</li>
</ul>
<h3>What I don't like</h3>
<ul>
<li>No apps. No kindle, for example. It's a shame that amazon did not create a dedicated kindle app for windows tablets. There are alternatives, for example there Kindle for PC (that is horrible) and kindle cloud reader (that doesn't work with no internet). I'm using Kindle for PC now.</li>
<li>Windows 10 can be used in two ways: tablet mode and desktop mode. Tablet mode is much slower than desktop mode, and by the way only some apps are really designed for it.</li>
<li>The launcher (aka the windows menu) is also crappy in tablet mode, I had to do a lot of rearranging.</li>
<li>The display is not the best ever. It works, and for what I have to do is fine, but modern smartphones have raised the bar and now I clearly see when a display is not 4k.</li>
</ul>
<p>I guess this tablet is great fot those that only use Word, Excel, Outlook, so students or corporate users. Not so great if one wants to do photo editing, or photo/video related operations, altough you can install whatever Adobe product you desire. The ecosystem on Windows is more oriented to PROs rather than to amateurs, this means that many stupid apps (like photo organizing or video editing) are sincerly missing.</p>
<p>Anyway, my father just said that he loves this tablet. He likes it more than his mac. He truly wants to swap his laptop for this, and honestly I think this could be a good idea. He is not a tech person, he only wants to get things done. I'll let you know if he'll go through this path!</p>
Chaotic systems
2020-02-26T00:00:00Z
https://michelenasti.com/2020/02/26/chaotic-systems.html
<p>a <strong>chaotic system</strong> is a system composed of many simpler systems, that are interconnected in a way such that the final result is impossible to connect to the subsystem forces.</p>
<p><img src="https://michelenasti.com/images/island-during-golden-hour-and-upcoming-storm-1118873.jpg" alt="" /></p>
<p>An example of such system is <strong>metereology</strong>: we know all the factors that are involved, like oceans, temperature, air, day/night, latitude, but still it is very hard to predict the outcome for very future dates.</p>
<p>However, the more we study them, the more we understand how they work and we gain advantage in predictions. Like, if we give more computing power, or add other subsystems into the equations, we gain more days of prediction in weather forecasting.</p>
<p>Another example is the <strong>human body</strong>: we know that some things may cause some diseases, but we're still far from saying that a person with a given DNA and behavior will surely develop a certain disease or not.</p>
<p>These kinds of systems are called <strong>first-level complex systems</strong>. These are the ones that are studied with more profit. The more we study, the more we gain.</p>
<p>But there's another category of systems that has intrigued my mind, and I honestly can't sleep at night thinking of them: <strong>second-level</strong> <strong>complex systems.</strong> In those systems, <em>the act of predicting or observing is messing up the prediction</em>. It's like, what if you knew that you're going to die by a plane crash? You would not take planes anymore, and that's an example of a prediction that is breaking the future.</p>
<p>There are at least two second-level complex systems around us that prevent us to make perfect predictions of the future. The first one is <strong>economics</strong>. Let's say we predict that the price of petrol will increase next month. What would we all do? buy petrol, like crazy, and the price increase will shift from next month to now. Prediction broken.</p>
<p>Luckily, there are disciplines like <strong>Game Theory</strong> that is trying to understand if there's an equilibrium for some systems, no matter what partecipants in the system do. So we're still trying to transform the second-level system to first-level!</p>
<p>Another example of a second level complex system is <strong>history</strong>. We may study causes to events and understand that, when some things happen, we exect a war or an insurrection to happen.</p>
<p>Let's suppose we know that next year a massive insurrection will happen, and sell this prediction to our (benevolent, but suspicious) dictator. He'll say, <em>ok, let's create some incentives to stimulate economics, let's open up on some civil rights, (but let's also prepare the army just in case)</em>. Next year arrives and no insurrection happens. The dictator calls and says: you bastard, you took my money and I believed you! Give me the money back and, by the way, you'll be executed tomorrow".</p>
<hr />
<p>These and other kind of stories like this are in the book <a href="https://amzn.to/3a5hB1T" title="Sapiens - a brief history of humankind">Sapiens - a brief history of humankind</a>, that I truly recomend. Very inspiring, full of theories that try to understand the past in a non-linear way.</p>
I wish I had a programmers' quarantine
2020-05-02T00:00:00Z
https://michelenasti.com/2020/05/02/i-wish-i-had-my-quarantine.html
<p>Hello devs, I wrote this article during COVID-19 quarantine, hopefully you'll read this post when it's all over and we'll think of this period as a not-so-painful memory.</p>
<p>Italy was one of the first countries to start a hard quarantine, and expecially in my region we closed restaurants, bar, gyms. Even walking out was problematic. So, in theory, we all had a lot of free time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/size/w2000/2020/03/FwUlnGszXSVDNjJ-800x450-noPad-1.jpg" alt="Credit for the image: freecodecap.org" /></p>
<p>In reality, something has changed in my life and it's something you usually don't associate to a programmer: I committed the sin of building a family, with a wife and a daughter👪</p>
<p>So yes, I was itching my hands thinking of all the side projects I could complete, books I could read, online meetups I could organize; in reality, my job already takes 8-9 hours of my day and after that I have to reconnect with the family.</p>
<p>So, time for my personal projects is very limited and I'm trying to use it wisely.</p>
<p>I'm using these days to fill some holes in my preparation, so for example <strong>I'm studying C again</strong>. At the time I first studied C in university, I did not understand many concepts very well. Now they feel like a natural concept, but back at the time it was the first meeting with a real programming language and it was too much for young Michele.</p>
<p>My plan is to understand the basics of how operating systems are made. I was reading a book about it, when the author started discussing about assembly and C and I felt the urge to get those concepts back in my mind again.</p>
<p>Here are some of the questions I left unanswered:</p>
<ul>
<li>Are C programs always compiled to one big application?</li>
<li>If not, How is it possible to compile a file into a library and use the library as an external resource in a C program?</li>
<li>How are strings handled?</li>
<li>Is everything global, even the functions declared in external files? What are the best practices to avoid name clashes?</li>
<li>What's the malloc/calloc/*alloc fuss?</li>
</ul>
<p>I know what you think, "<em>duuude these are very basic questions</em>" and really they are, but I was in some kind of learn-and-forget state during university and also it was my very first exam, it didn't go well: for example, once the vote was registered, I asked the teacher to redo the exam in order to "upgrade" my vote. He looked at me very calmly and said: "this is not how university works..."</p>
<p>To conclude, if you have answers to the above questions, feel free to answer in the comments. If you're interested in detailed answers, I'll try to write some articles on C on my blog. I'm reading the super-famous K&D book, hopefully I should find all my answers there ;)</p>
How Coronavirus affected my life
2020-05-19T00:00:00Z
https://michelenasti.com/2020/05/19/how-coronavirus-affected-my-life.html
<p><strong>I bet none of you predicted to live the months we have just lived</strong>. I expected to see an earthquake, an eruption by the Vesuvius, a nuclear war, but for sure not a <em>pandemy</em>.</p>
<p>Now that we're approaching summer, infections are decreasing and the quarantine seems to be a thing of the past. To be clear, I am safe, all my family and my colleagues are safe, so we didn't have any health troubles during this period.</p>
<p>But I cannot lie about the psycological effects of the quarantine. I feel it, in the way I have lived my family life, my work life, and my public life.</p>
<p><img src="https://michelenasti.com/images/man-in-white-t-shirt-holding-black-smartphone-4113948.jpg" alt="" title="days seem to not pass" /></p>
<p>My <strong>family life</strong> was one of the most affected. My wife temporary stopped working so she had to take care of my three-years-old daughter, that stopped going to pre-school. If schools do not open in september, we're fucked. My wife loved her work and she's doing a lot of work to keep in touch with customers even during quarantine and with a kid jumping on the sofa. So, when I finish my day job, it's my turn to take care of a baby that has been closed into four walls all day long, with the result that she's full of energy while I am exhausted.</p>
<p>Regarding my <strong>work life,</strong> I am lucky my job wasn't cut. Of course, working in IT means we can work from everywhere, and luckily I am also a remote worker since some years, so I experienced a little impact. Other programmers, and more broadly other categories of workers, were not so lucky. Anyway, I must admit that during the first days of the quarantine I was in panic. The web was full of coronavirus news, and discussions were all about infections, laws, government actions, in a way that it was really hard to concentrate and do my business. I am glad the worse has passed.</p>
<p>The most important change in this area is that I am no longer working from a coworking space. I had to suspend my subscription, because of the new family asset and also because there are no more seats for everybody, given that we must be at two meters from each other. I loved being in a coworking space and have people around. <em>Remote work is fine, but having a social life is much more important.</em></p>
<p>So, speaking of my <strong>public life</strong>, this is the area I am regretting most. I loved to go out, visit friends, have dinner in pizzerias and restaurants, bars and cafés, taking trips to beautiful italian places etcetera. So, when quarantine started, I tried to get in touch with all my friends, but I admit <strong>I slowly got used to the situation</strong>, and now the first feeling I have when I have to go out is that It's a risky, and I have to do it only if urgent. Luckily, restaurants are slowly opening but they have to distantiate people so they'll have less seats per table. And prices will increase.</p>
<p>So, this was my quarantine and these are the effects I still feel. I hope to read this post some day in the future, and think of how lucky I am.</p>
Serve HTTPS requests with Express and NodeJS
2020-05-23T00:00:00Z
https://michelenasti.com/2020/05/23/serve-https-requests-with-express-and-nodejs.html
<p>One of the things I do, on my job, is to maintain a javascript library to display ads. Customers will inject this script on their page and <em>voilà</em> they start displaying ads.</p>
<p>Everything works nice and easy except debugging. One of the problems is that we build this big chunk of javascript with customer's data, a bunch of other open source libraries, all concatenated in one file (there is a reason why we do that - we are not crazy).</p>
<p>So, when we have to debug the library, we usually <em>generate</em> a modified version (uncompressed, or with sourcemaps, or with a different configuration...) and then we inject it into the page. To do that, we use several thecniques:</p>
<ul>
<li>via <a href="https://www.requestly.in/" title="Requestly.in">Requestly</a> (a firefox and chrome plugin) it is possible to redirect a request to localhost</li>
<li>by specifying an entry in <code>/etc/hosts</code> we can also redirect traffic for a domain to aother ip, like localhost</li>
</ul>
<p>The problem is when our target website is on <strong>https</strong>. As you know, when the page is served on a secure channel, all other requests must be https too. This means that if I require <strong><code>https</code></strong><code>://michelenasti.com</code> I cannot add a script tag pointing to <strong><code>http</code></strong><code>://cdn.google.com/jquery.js</code> - the browser will refuse to serve the file and will display a warning in console.</p>
<blockquote>
<p>The opposite is not true: if the page is on http, you can link to https files.</p>
</blockquote>
<p>So I needed to serve my files over https. How to do that?</p>
<p><img src="https://michelenasti.com/images/person-holding-white-scroll-2292837.jpg" alt="Yes, you need to "generate" a certificate. " title="certificate" /></p>
<h2>Generate the certificates</h2>
<p>First of all, we need to <strong>generate a certificate</strong> that will be used by our https server. In the same folder of express <code>index.js</code>, run this command on mac or linux:</p>
<pre class="language-shell"><code class="language-shell">$ <span class="token function">sudo</span> openssl req <span class="token parameter variable">-x509</span> <span class="token parameter variable">-nodes</span> <span class="token parameter variable">-days</span> <span class="token number">365</span> <span class="token parameter variable">-newkey</span> rsa:2048 <span class="token parameter variable">-keyout</span> ./selfsigned.key <span class="token parameter variable">-out</span> selfsigned.crt</code></pre>
<p>The program will ask you to answer some questions on the issuer of the certificate.</p>
<p>This will generate two files, <code>selfsigned.key</code> and <code>selfsigned.crt</code>, that can be used by any webserver in the world, but for now we'll use in nodejs.</p>
<p><strong>This step is only required once</strong>. Generate and forget :)</p>
<h2>Use the certificates in express</h2>
<p>in the same folder of the generated certificates, create a file <code>index.js</code>:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'express'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> http <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'http'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> https <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'https'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// read the certificate files previously created and create </span><br /><span class="token comment">// options object for https server </span><br /><span class="token keyword">var</span> key <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readFileSync</span><span class="token punctuation">(</span>__dirname <span class="token operator">+</span> <span class="token string">'/selfsigned.key'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> cert <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readFileSync</span><span class="token punctuation">(</span>__dirname <span class="token operator">+</span> <span class="token string">'/selfsigned.crt'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> options <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">key</span><span class="token operator">:</span> key<span class="token punctuation">,</span><br /> <span class="token literal-property property">cert</span><span class="token operator">:</span> cert<br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// this handler will respond to all GET requests. </span><br /><span class="token comment">// It will respond with the file that matches the url. </span><br /><span class="token comment">// For example http://localhost:3000/index.js </span><br /><span class="token comment">// will respond with this very file. </span><br /><span class="token keyword">const</span> <span class="token function-variable function">handler</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> res<span class="token punctuation">.</span><span class="token function">sendFile</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>req<span class="token punctuation">.</span>url<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">root</span><span class="token operator">:</span> <span class="token string">'.'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br />app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'*'</span><span class="token punctuation">,</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// set the server to respond on http and https </span><br /><span class="token keyword">var</span> httpServer <span class="token operator">=</span> http<span class="token punctuation">.</span><span class="token function">createServer</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">var</span> httpsServer <span class="token operator">=</span> https<span class="token punctuation">.</span><span class="token function">createServer</span><span class="token punctuation">(</span>options<span class="token punctuation">,</span> app<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// set the addresses. This will start the program! </span><br />httpServer<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />httpsServer<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token number">8443</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p><strong>The first time you run this program, browsers will ask you if you trust this certificate</strong>. Then, the server will work flawlessly.</p>
<p>To conclude, I want to point out <strong>how easy</strong> it is to create an http(s) server with few lines of code and nodejs.</p>
<p>And also, that <strong>this server should not end in production</strong>. what if a hacker will request `https://your_server:8443/selfsigned.key ? Or some other random file on your hard disk? enjoy :)</p>
Sarò controcorrente, ma a me piaceva andare in ufficio
2020-05-25T00:00:00Z
https://michelenasti.com/2020/05/25/saro-controcorrente-ma-a-me-piaceva-andare-in-ufficio.html
<p>Non fraintendetemi, non mi piaceva mica il traffico o il tempo perso per raggiungere l'ufficio, o il capo spocchioso. A me piaceva proprio conversare con i miei colleghi.</p>
<p>Ironico il mondo; ormai lavoro da 4 anni da remoto ed é impossibile per me tornare indietro, visto che nessuna azienda della zona sembra voler (o poter) offrire quello che mi offre il lavoro dall'estero. Dal punto di vista professionale mi ritengo molto fortunato per la complessità e l'impatto che il mio lavoro ha. Non il solito gestionale, insomma.</p>
<p>Eppure ricordo ancora quando andavo in ufficio. <strong>Il caffé coi colleghi, le riunioni fiume, le pizze e i panini post lavoro. Le discussioni, i problemi risolti alla lavagna.</strong> Queste cose qui, col lavoro remoto, tendono a diventare un dolce ricordo del passato.</p>
<p><img src="https://michelenasti.com/images/top-view-photo-of-people-near-wooden-table-3183150.jpg" alt="" /></p>
<p><strong>É probabile che io stia idealizzando i miei ricordi</strong>, quindi provo a ritornare sul pianeta terra: l'autobus - che certi giorni ci metteva anche due ore per entrare o uscire da Napoli - era uno strazio, ma mi permetteva di studiacchiare o leggere; I capi, poi, erano persone <em>strane</em>, il cui obiettivo non era quello di fare un buon prodotto, bensì quello di non rompere le palle a cui stava sopra di loro. Assurdo.</p>
<p>In questi giorni di Covid <strong>quasi tutto l'universo informatico italiano ha dovuto forzatamente provare il lavoro da remoto, e</strong> <strong>molti miei amici hanno appunto chiesto di poter continuare così</strong>. Loro non sanno a cosa stanno per rinunciare, io si: i nuovi colleghi potrebbero vivere a migliaia di km di distanza, e un invito a un panino o una pizza in una città fuori dal proprio giro potrebbe non arrivare mai più.</p>
<p>Forse sto idealizzando anche i colleghi... Certo, ne ho avuti di deficienti e anche di stronzi, ma quante confidenze iniziavano con "X oggi ha proprio rotto" o di "Y ha detto questo al capo"... Un buon numero di loro era più squisitamente medio, dal punto di vista tecnico, ma decisamente decente dal punto di vista della normalità, quel tipo di normalità che ti fa parlare di calcio, politica e donne come se ci si conoscesse da una vita.</p>
<p><strong>Poi ci sono quelli bravissimi, quelli smart. Se sono anche simpatici hai fatto bingo</strong>, hai trovato delle persone che non devi perdere per il resto della tua vita, perché ti insegneranno tutto quello che sanno senza fartelo pesare. E sono i primi a rispondere a un invito a uscire. Queste persone qui io le adoro! Ricordo le conversazioni fatte a giornata conclusa, mentre si tentava di far partire il server che rispondeva picche a ogni nostro tentativo.. Ragionare con loro ti fa sentire in una fiction in cui devi smascherare l'assassino formulando ipotesi, e che gioia quando alla fine ci riesci!</p>
<p>Insomma, il mio sogno é di tornare prima o poi a lavorare fisicamente con quelle 4-5 persone con cui mi trovo bene, magari su problemi non banali e vivendo anche una vita sociale appagante. Il lavoro da remoto é fantastico, su questo non ci piove. <strong>Ma avere degli amici che puoi vedere tutti i giorni con la scusa del lavoro, di più.</strong></p>
My first Visual Studio Code Extension, create-link-to-git-server
2020-06-15T00:00:00Z
https://michelenasti.com/2020/06/15/my-first-visual-studio-code-extension-create-link-to-git-server.html
<p>You know when you're chatting with a colleague and <strong>you want to point</strong> <strong>out</strong> <strong>a specific line in the git repository?</strong> For example, <strong>a specific line of a specific branch of a specific fork</strong>? It's a time-wasting process to open the browser, open the git hosting website, navigate to the file, and create the link; so I decided to create an extension for Visual Studio Code.</p>
<p>Here's what I learned.</p>
<p><img src="https://michelenasti.com/images/visual-studio-code-logo.png" alt="" /></p>
<h2>Wait, does it work?</h2>
<p>Yes! This extension is very simple. It adds a new item (<code>Get link to Git server</code>) in the right-click menu. When you click, the extension will copy in the clipboard a link to that line that you can paste wherever you want.</p>
<p>It supports ssh and https repositories.</p>
<p>You can download the extension by</p>
<ul>
<li>compiling it yourself from <a href="https://github.com/musikele/create-link-to-git-server">github</a></li>
<li>exploring it on <a href="https://marketplace.visualstudio.com/items?itemName=musikele.create-link-to-git-server">visual studio code marketplace</a></li>
<li>installing it directly from VSC by searching for <code>create-link-to-git-server</code></li>
</ul>
<p>Here's a demo:</p>
<p><img src="https://raw.githubusercontent.com/musikele/create-link-to-git-server/master/demo.gif" alt="" /></p>
<h2>Wasn't there another extension that does exactly that?</h2>
<p>You may be thinking of the super popular extension GitLens, of which I am a huge fan and user. Anyway, GitLens has some issues with my setup:</p>
<ul>
<li>At my company we use a private git server, and gitlens doesn't play nice with that.</li>
<li>With public famous repositories, like github, GitLens produces a link to the commit, so you loose the information about the branch and the fork, that I find very useful.</li>
</ul>
<p>Anyway my extension does not block GitLens in any way, so feel free to use both :D</p>
<h2>How do you exactly write a VSC extension?</h2>
<p>Folks at Microsoft have published a ton of articles regarding how to build an extension for Visual Studio Code. The starting point is this: <a href="https://code.visualstudio.com/api/get-started/your-first-extension">Your First Extension</a>. It will create a simple "Hello World" extension that will show a message on screen.</p>
<p>So: you need to write the function in a Typescript file, and then specify how this function should be activated in <code>package.json</code> .</p>
<h3>Code</h3>
<p>All the code will be in a typescript file called <code>src/extension.ts</code> that contains the following blocks:</p>
<ul>
<li>an <code>activate</code> function , that is run at VSC startup</li>
<li>inside the activate function, we register the command we want to show; in my case, I called <code>registerTextEditorCommand</code> that contains the body of the code. Here I do all my logic, like getting git info from VSC, and building the link. More info on those commands <a href="https://code.visualstudio.com/api/references/vscode-api#commands">here</a></li>
</ul>
<h3>Package.json</h3>
<p>Creating the command is not enough. Now we must specify how to activate those commands. This is done in <code>package.json</code> file by specifying custom properties. I admit that the documentation for this is messy, overly descriptive, and not easy to find/digest/understand without some research in stack overflow.</p>
<p>To be precise, Here's a snippet from my package.json:</p>
<pre class="language-json"><code class="language-json">...<br /><span class="token property">"activationEvents"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token string">"onCommand:create-link-to-git-server.helloWorld"</span><br /><span class="token punctuation">]</span><span class="token punctuation">,</span><br />...<br /><span class="token property">"contributes"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"menus"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"editor/context"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"when"</span><span class="token operator">:</span> <span class="token string">"editorTextFocus"</span><span class="token punctuation">,</span><br /> <span class="token property">"command"</span><span class="token operator">:</span> <span class="token string">"create-link-to-git-server.helloWorld"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"commands"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"command"</span><span class="token operator">:</span> <span class="token string">"create-link-to-git-server.helloWorld"</span><span class="token punctuation">,</span><br /> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Get link to Git server"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span><br />...</code></pre>
<ul>
<li>the <code>activationEvents</code> section specifies when this extension will be activated. In my case, it will be activated when the command <code>create-link-to-git-server.helloWorld</code> is triggered. <a href="https://code.visualstudio.com/api/references/activation-events">Here are all the possible activation events you can use</a>.</li>
<li>The <code>contributes</code> section specifies how you extension contributes to Visual Studio Code. In this section, we can add menu items, commands, themes, whatever. What I do here is to specify two ways of modifying VSC: by adding a menu item in the editor menu, and by specifying a command that is triggered on Cmd+Shift+P (ctrl+shift+p). <a href="https://code.visualstudio.com/api/references/contribution-points">Here's the whole list of contribution points</a></li>
</ul>
<h2>How long did it take to write this extension?</h2>
<p>In total, I worked on this about 4-6 hours to get the bulk of the code working. As I said, I had no idea on how to</p>
<ul>
<li>get Git information from VSC</li>
<li>add a menu item in VSC</li>
<li>write something in the clipboard</li>
</ul>
<p>For all this, Stack Overflow was my friend.</p>
<p>Then, there's the releasing part ....</p>
<h2>What about releasing</h2>
<p>To release an extension on Visual Studio Code marketplace, you have to do a bunch of things first:</p>
<ul>
<li>write a good Readme / documentation, that explains clearly what the extension does. This is what is shown to your users when they search for it. I also added a GIF to show the behaviour of the extension.</li>
<li>Install a tool called <code>vsce</code>, create an account on Azure Deploys, authenticate my own pc, and publish. <a href="https://code.visualstudio.com/api/working-with-extensions/publishing-extension">This has a tutorial</a>, too, and honestly I found it a bit complicated. The good news is that, once the account is set up and the code is committed, you only have to run <code>vsce publish <sem_ver></code> and wait a couple of minutes.</li>
</ul>
<hr />
<p>So, what can be another "missing" extension? What else can we build for developers that can be extremely useful? Let me know :)</p>
What is a WAV file?
2020-09-06T00:00:00Z
https://michelenasti.com/2020/09/06/what-is-a-wav-file.html
<p>Short post: last week I came across a wonderful video about <a href="https://www.youtube.com/watch?v=udbA7u1zYfc">how WAV files are made</a>, and how to create one from scratch, directly from NodeJS. The author, <a href="https://www.youtube.com/channel/UC56l7uZA209tlPTVOJiJ8Tw">Low Level Javascript</a>, is really investing time creating quality content that is direct, clear, and coincise. Consider donating some money to his patreon if you find his videos useful.</p>
<p>Before this video I had a general idea of how audio was represented, but when I saw that the author generated an entire playable wav file directly from the command line, writing byte by byte, I felt like I was learning something. And <strong>this, my friends, is what I want to pay for: to feel the click inside my brain that I'm actually discovering</strong> (...not learning) <strong>something</strong>.</p>
<p>I'd now like to know the second part of the story: how bits become analog signals that are converted to sound waves by audio cards? Is the physics behind this process understandable? If you have a good video for that, let me know.</p>
<p><iframe width="560" height="315" src="https://www.youtube.com/embed/udbA7u1zYfc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
How to read and write from STDIN and STDOUT in GO
2020-09-16T00:00:00Z
https://michelenasti.com/2020/09/16/how-to-read-and-write-from-stdin-and-stdout-in-go.html
<p>At my current job we have some projects that were realized using the <strong>Go programming language</strong>. The cool thing is, programmers that have used it are in love with it, and these applications never crash, not even under heavy load. This is great, for example, for applications that have to process a stream of data as fast as possible.</p>
<p><img src="https://michelenasti.com/images/golang.png" alt="" /></p>
<p>So, I decided to go through HackerRank and do the 30 days challenge. I do not expect to become a True Expert© but, at least, I'd like to catch up with the syntax and all the rest.</p>
<p>(Probably, in the long run, we're going to sunset these Go projects because we got acquired by another company, and their main programming language is Java, sooooo....)</p>
<p>I will not go through installing the go compiler, <em>Go figure out</em> by yourself. But this hello world may give you (and me) a sense of what's going on here.</p>
<h2>On the educative task of explaining Hello World</h2>
<p>When I was at university my Java teacher asked students to write the best-possible explanation of Java's Hello World, that would win a Java book (Java 1.4, I'm that old). So I wrote a 4 pages essay explaining <em>everything</em> (keywords, exceptions...) and yes, I own that book now :)</p>
<p>This "exercise" is really valid and I encourage everyone to do it. You will do a lot of research to explain tiny details that usually do not seem to have much importance. So, for example, it took me 4-5 hours to write this article. The outcome is that I feel confident of what I learned.</p>
<h2>Things you should know before we write some code</h2>
<p><strong>Go is a compiled programming language</strong>. This means that to run your program you first need to compile it using the <code>go</code> executable.</p>
<p>Go source files have the <code>.go</code> extension. So, to compile a file:</p>
<pre class="language-shell"><code class="language-shell">$ go build hello-world.go <br />$ <span class="token function">ls</span> <br />hello-world hello-world.go </code></pre>
<p>Go compiler will create a <code>hello-world</code> executable that we can lunch, on Linux and Mac systems, by running <code>./hello-world</code> .</p>
<p>Some may say that it's boring to compile & launch so <strong>Go offers the <code>run</code> mode, that will execute the two steps for you:</strong></p>
<pre class="language-shell"><code class="language-shell">$ go run hello-world.go <br /><span class="token punctuation">..</span><span class="token punctuation">..</span> <span class="token punctuation">(</span>program output here<span class="token punctuation">)</span> </code></pre>
<p>And finally: <strong>go has an official formatting tool</strong>. This means that you cannot decide how many spaces (or tabs), or how long your lines should be, etc. Smart IDEs like Visual Studio Code with the Go extension will automagically run the formatting tool for you at every save. But if you want to run it from the command line:</p>
<pre class="language-shell"><code class="language-shell">$ go <span class="token function">fmt</span> hello-world.go </code></pre>
<p>No more issues on git merge :)</p>
<h2>Let's go back to the source code</h2>
<p>The exercise track:</p>
<blockquote>
<p>save a line of input from stdin to a variable, print <code>Hello, World.</code> on a single line, and finally print the value of your variable on a second line.</p>
</blockquote>
<p>Here's the source code:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">package</span> main<br /><br /><span class="token keyword">import</span> <span class="token punctuation">(</span><br /> <span class="token string">"bufio"</span><br /> <span class="token string">"fmt"</span><br /> <span class="token string">"os"</span><br /><span class="token punctuation">)</span><br /><br /><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> reader <span class="token operator">=</span> bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdin<span class="token punctuation">)</span><br /> message<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> reader<span class="token punctuation">.</span><span class="token function">ReadString</span><span class="token punctuation">(</span><span class="token char">'\n'</span><span class="token punctuation">)</span><br /><br /> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Hello, World."</span><span class="token punctuation">)</span><br /> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><br /><span class="token punctuation">}</span></code></pre>
<p>Let's break up and analyse the code in parts.</p>
<h3>the package declaration</h3>
<pre class="language-go"><code class="language-go"><span class="token keyword">package</span> main</code></pre>
<p><strong>All go code must declare its package.</strong> Executable files must be in a <code>main</code> package, and the first function that is executed on the first run is the <code>main</code> function.</p>
<h3>Import block</h3>
<pre class="language-go"><code class="language-go"><span class="token keyword">import</span> <span class="token punctuation">(</span><br /> <span class="token string">"bufio"</span><br /> <span class="token string">"fmt"</span><br /> <span class="token string">"os"</span><br /><span class="token punctuation">)</span></code></pre>
<p>Here, we are importing three packages from the go standard library:</p>
<ul>
<li><code>fmt</code> is the <a href="https://golang.org/pkg/fmt/">formatted I/O library</a> and contains functions to read and write from I/O like printf and scanf in C.</li>
<li><code>bufio</code> is the package that will perform <a href="https://golang.org/pkg/bufio/">buffered i/o operations</a>. basically, we want to read a bunch of characters at a time, and this is the package that contains the easiest functions.</li>
<li><code>os</code> provides a <a href="https://golang.org/pkg/os/">platform-independent interface</a> to operating system functionality.</li>
</ul>
<p>I must be honest with you, in my first attempt to write this block, I wrote:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">import</span> <span class="token string">"fmt"</span><br /><span class="token keyword">import</span> <span class="token string">"bufio"</span><br /><span class="token keyword">import</span> <span class="token string">"os"</span></code></pre>
<p>This is legal syntax, but the go formatter decided that wrapping all packages inside a single <code>import</code> declaration is better. If the formatter goes with the other syntax, it's probably better to use it from the start.</p>
<h3>function declarations</h3>
<pre class="language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token operator">...</span><br /><span class="token punctuation">}</span></code></pre>
<p>Another piece of syntax from go: to declare functions we write <code>func</code> followed by the function name. If the file must be executable, the function must be called <code>main()</code> and be in the <code>main</code> package.</p>
<p>You may ask, "<em>what is the syntax if I want to pass arguments? and return values?</em>" Ok, here's a slightly more complex example:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">addMult</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span>b <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> a<span class="token operator">+</span>b<span class="token punctuation">,</span> a<span class="token operator">*</span>b<br /><span class="token punctuation">}</span></code></pre>
<p>wtf? well, this means that this function accepts two parameters in input (<code>a</code> and <code>b</code>) and will return <strong>two</strong> values, that you can assign. We'll see an example in the next block.</p>
<h3>Read input from STDIN</h3>
<pre class="language-go"><code class="language-go"><span class="token keyword">var</span> reader <span class="token operator">=</span> bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdin<span class="token punctuation">)</span><br />message<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> reader<span class="token punctuation">.</span><span class="token function">ReadString</span><span class="token punctuation">(</span><span class="token char">'\n'</span><span class="token punctuation">)</span></code></pre>
<p>Here we are declaring <code>reader</code> variable with <code>var</code> and <code>message</code> variable without <code>var</code>. Why?</p>
<p>To only declare a variable, without initializing, you can use the keyword <code>var</code> followed by the variable name; you must also declare a type.</p>
<p>If the type can be inferred by the assigning expression, it can be omitted.</p>
<p>If the variable is initialized and assigned in the same moment, Go offers the shorthand syntax via <code>:=</code> that allows to avoid the <code>var</code> keyword. So the first line may be written as:</p>
<pre class="language-go"><code class="language-go">reader <span class="token operator">:=</span> bufio<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>os<span class="token punctuation">.</span>Stdin<span class="token punctuation">)</span></code></pre>
<p>As I specified before, the <code>bufio</code> library contains functions that allow to read in a buffer. The buffer we are creating is reading from STDIN, that is a common name for the Standard Input. Basically, what the user types in the terminal.</p>
<p>Once we get a reference to the <code>reader</code>, we use it to read a string using the method <code>ReadString()</code>. ReadString accepts a character (that is wrapped in single quotes, <code>''</code> instead of strings that use double quotes, <code>""</code>) that will be used to match the end of the buffer line. But... what's on the left side of the assignment?</p>
<pre class="language-go"><code class="language-go">message<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> <span class="token operator">...</span></code></pre>
<p>We just hit our first multi-return function. ReadString returns two values, the data read and the error; we <em>should</em> take care of the error variable (in Go, if you declare a variable and you will not use it, the program will not compile at all), but if we want to skip the variable assignment, we can simply set to <code>_</code> (underscore) and Go compiler will stop protesting. So, in a scenario like reading from a file or from the network, where obviously something may go wrong, skipping the error check is not a good idea. In this case, given the simplicity of the program, we take our responsibilities as grown adults.</p>
<h3>Writing to STDOUT</h3>
<p>Outputting data is much simpler:</p>
<pre class="language-go"><code class="language-go">fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Hello, World."</span><span class="token punctuation">)</span><br />fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span></code></pre>
<p>In this snippet, we are writing "Hello World" followed by the message we captured at the previous step. That's it. Program ended.</p>
<h2>Where is the power of Go?</h2>
<p>You may not see it from this very simple program, but:</p>
<ul>
<li>being compiled, and strongly typed, many errors will be caught at compile time.</li>
<li>multi-return values allow for the error-checking pattern that is verbose, but produces some very robust code.</li>
<li>Go shines on multithreaded applications using "channels", more on that in the next articles. As I said, my colleagues wrote a super-fast data processor that never breaks, even under heavy load.</li>
</ul>
<p>Below I leave some links that I checked while writing this article. Explore them like I did. Bye!</p>
<hr />
<p>More resources:</p>
<ul>
<li><a href="https://golang.org/doc/install">how to install go for your OS</a> (golang.org)</li>
<li>many <a href="http://zetcode.com/golang/readinput/">other ways to read input in Go</a> (from zetcode.com)</li>
<li><a href="https://golangbyexample.com/double-single-back-quotes-go/">difference of quotes in Go</a> (from golangbyexample.com)</li>
<li><a href="https://medium.com/rungo/the-anatomy-of-functions-in-go-de56c050fe11">anatomy of functions in go</a> (by runGo)</li>
<li><a href="https://gobyexample.com/variables">Go by example: variables</a></li>
<li><a href="https://golang.org/pkg/bufio/#Reader.ReadString">ReadString specification</a> (golang.org)</li>
</ul>
[BOOK] What If?: Serious Scientific Answers to Absurd Hypothetical Questions
2020-10-09T00:00:00Z
https://michelenasti.com/2020/10/09/book-what-if-serious-scientific-answers-to-absurd-hypothetical-questions.html
<p>This book is just fun.</p>
<p><img src="https://michelenasti.com/images/img_20200928_202854.jpg" alt="" /></p>
<p><strong>What If?: Serious Scientific Answers to Absurd Hypothetical Questions</strong> is a series of scientific and absurd questions, answered with a mix of seriousness and funny sarcasm. Some questions whose answers I found hilarious are:</p>
<ul>
<li>What if you throw a baseball to 99% speed of light?</li>
<li>What happens to earth if all the population of the world stay in the same place and jump simultaneously?</li>
<li>What if a earthquake of 15th grade magnitude strikes the earth?</li>
<li>(very actual) will we be able to remove common flu from the world by practicing physical distance?</li>
</ul>
<p>... And many, many more.</p>
<p>You don't need a phisics degree to understand the logics. Sometimes, it's more about human psichology than other. And indeed, it's the human aspect that made this book so enjoyable.</p>
<p>The author, Rundall Munroe, authors also the super popular nerdy comic xkcd on the Web. The book is full of his drawings that will, at least, make you laugh.</p>
<p><img src="https://michelenasti.com/images/img_20200928_203357__01.jpg" alt="" /></p>
<p>You can find the book here on the italian <a href="https://amzn.to/3iMOd4q">Amazon</a>.</p>
<p><strong>Note:</strong> from the units of measure's perspective, the author is familiar with both the International and the Imperial System; you may find references to both. I wonder how we got here with this confusion in how to measure things; and more importantly, why just a couple of countries refuse to adhere to an international standard? I guess it's because they believe to be the greatestest and don't want to be them changing. I mean, this stuff should be used to fix communication problems in business, science, transports, etc. So yes, why are we complicating our lives like that? I'll never know. As the author of the book wrote,</p>
<p><img src="https://imgs.xkcd.com/comics/standards.png" alt="https://imgs.xkcd.com/comics/standards.png" /></p>
<p>:)</p>
A gently intro to MVC Frameworks
2020-12-27T00:00:00Z
https://michelenasti.com/2020/12/27/a-guide-to-mvc-frameworks.html
<p>In the last few years I've worked or played with a bunch of MVC frameworks: <strong>Laravel</strong>, <strong>Ruby on Rails</strong>, <strong>Django</strong>, and <strong>AdonisJS</strong>.</p>
<p>With two of them (Laravel and Dango) I also have some experience in production environments. Laravel is the one I know most.</p>
<p>So, what do they all share in common? What's the difference between them and Express, Flask, Sinatra? This article will try to explain the philosophy around those tools.</p>
<h2>Why do we need these tools?</h2>
<p>The web is not only a matter of serving the right file to the user; you usually also want to:</p>
<ul>
<li>pass arguments, values, cookies, more generally data with the request;</li>
<li>customize the output: not only in the format (plain html, or json, or ... xml if you are kink enough), but also the data in it (if you are a logged user you may want to see more things than an anonymous user; etc)</li>
<li>handle security, that is nowadays a first class citizen</li>
<li>handle authorization, with one of the multiple authorization schemes hat have appeared in the last few years</li>
<li>be able to scale: in the number of concurrent users, in the size of the handled data, and in the organization of the code itself.</li>
</ul>
<p>The key point here is,<strong>people got tired of solving the same problem over and over and invented a set of tools that standardized/simplified the developer's job</strong>, allowing to focus more on the business logic than on the details.</p>
<h2>So what's this MVC stuff?</h2>
<p>M stands for <strong>Model</strong>, and means the data that is manipulated by the framework. In current web frameworks the Model usually corresponds to classes that map to a database (E.g. <code>Person</code> class is linked to the <code>persons</code> table).</p>
<p>V stands for <strong>View</strong> and means the way the data is presented to the user. In my small dev life I've seen many ways to represent this part: JSON responses for example, or plain HTML. Usually, modern MVC tools offer a template system so you can write the <em>products.html</em> page once and then inject the data to be displayed.</p>
<p>C is for <strong>Controller</strong>, the part that will do the dirty job. When a request arrives, the controller will validate the input, fetch the database, apply business rules, and pass it to the right view. Or it will trigger some other systems. <em>This is the part you'll write in some programming language</em>.</p>
<h2>Opinionated vs Unopinionated</h2>
<p>Instead of referring to MVC vs Non-MVC frameworks, I think <strong>the best classification is between <em>Opinionated</em> vs <em>Unopinionated</em></strong>. Opinionated frameworks are those that are presented in this article; they give to the dev a lot of pre-made tools with sensible defaults that will do all the heavy lifting. Sad to say, but the developer's job is sometimes just to configure these tools to make them work as needed.</p>
<p>With <em>unopinionated frameworks</em>, you are alone with a tool that handles requests and responses. You may need to write all the tools you need, and end up creating a framework yourself. An example: you may need to decide how to access the database, or handle authentication... In this category I see <strong>Express</strong>, <strong>Flask</strong>, <strong>Sinatra</strong>.</p>
<h2>The router in MVC</h2>
<p>Almost all the opinionated frameworks expose a functionality called <em>router</em>. Similarly to phisical routers, these pieces of software will understand the URL of a request and will route to the right controller. One can create a route for <code>/products/123</code> and the router will send this request to <code>ProductController</code> calling the appropriate function and passing the parameter <code>123</code> as argument. Isn't this lovely?</p>
<p>So when I have to debug an application I usually start from here; I try to understand where the router configuration is (usually in <code>routes.php</code> or <code>urls.py</code>), and where the route I'm interested will point, and follow it.</p>
<h2>The ORMs</h2>
<p><strong>ORM</strong> stands for <em>Object Relational Mapping</em> and is a framework that allows to express sql queries using class constructs. Almost every major framework ships an ORM that is perfect for simple and sometimes elaborated queries. Basically the Model classes will extend a base class that contains all the boilerplate to read from the DB table and perform queries. For example, in Laravel:</p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important"><?php</span><br /><br /><span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Database<span class="token punctuation">\</span>Eloquent<span class="token punctuation">\</span>Model</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">class</span> <span class="token class-name-definition class-name">Flight</span> <span class="token keyword">extends</span> <span class="token class-name">Model</span><br /><span class="token punctuation">{</span><br /> <span class="token comment">//</span><br /><span class="token punctuation">}</span></span></code></pre>
<p>The class <code>Model</code> is the base class that will give to <code>Flight</code> all the fancy methods, like:</p>
<pre class="language-php"><code class="language-php"><span class="token comment">// Retrieve a model by its primary key...</span><br /><span class="token variable">$flight</span> <span class="token operator">=</span> <span class="token class-name static-context">Flight</span><span class="token operator">::</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Retrieve the first model matching the query constraints...</span><br /><span class="token variable">$flight</span> <span class="token operator">=</span> <span class="token class-name static-context">Flight</span><span class="token operator">::</span><span class="token function">where</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'active'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">first</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>Template systems</h2>
<p>Almost every MVC framework use a template system, so you can create HTML files with lower effort, even though it's nowadays very easy to just respond with JSON. Usually it only depends on what you return from the controller.</p>
<h2>Command line utilities</h2>
<p>Laravel has <strong>artisan</strong>, Python has <strong>manage.py</strong>, Rails has <strong>rake</strong>. These three command line utilities do <em>a lot</em>. They can:</p>
<ul>
<li><strong>create and execute migrations</strong>, that are operations that will change the structure of your database. You may create a migration to add a new table or change a column., for example. You can also rollback if something doesn't go as planned. All of that without writing SQLs directly on the DB server. We use this feature on a daily basis during development.</li>
<li>Likewise the migrations, sometimes you only want to add data in tables, like the values of a select box. This process is called <strong>seeding</strong>.</li>
<li>Put the server on <strong>maintenance mode</strong> and resume</li>
<li><strong>create boilerplate</strong> (models, controllers, views...) from command line, so that you only have to fill those generated files</li>
<li><strong>execute tests</strong></li>
<li><strong>create custom commands</strong>; for example, we usually create commands for operations that should be triggered by cron, or long-lasting batch commands.</li>
<li>...and much more! Every tool exposes more or less capabilities.</li>
</ul>
<h2>The tool you'll use more: the documentation</h2>
<p>I cannot stress out how important is documentation when using those frameworks. It's impossible to remember all the possible commands and configurations. You end up searching through docs, and if those are well written, you don't need to pay a visit on stack overflow right away. If you write an MVC tool and it doesn't have an <em>excellent</em> documentation, with a lot of examples, it will probably not be used by anyone.</p>
<h2>Standardization</h2>
<p>Another thing that pays off is that, no matter who'll work on what, all the code will look the same, and will be organized the same way. This has some value, since those kind of systems must be maintained over a span of several years, while the average dev only works only 2 years in the same company :)</p>
<h2>Conclusions</h2>
<p>I hope I don't have expressed concepts that are too trivial for the average reader; these are the basic info I wish somebody told me at the time.</p>
"You should have been blind by now" - An interview with a developer who is losing his sight
2021-01-28T00:00:00Z
https://michelenasti.com/2021/01/28/no-sight-developer.html
<p><strong>Have you ever thought of doing your daily job without sight</strong>? I must admit, I would not have any clue of where to start, but a very close friend of mine is going exactly through this and I think it's worth to share his story.</p>
<p>When I met Weber in 2006, we were both exchanging computer science students in Seville, Spain. I just arrived there, did not speak a word of spanish, and was looking for a room to rent. We were visiting the same flat and there was only one room available, a double room. So, after 5 minutes, we both decided to take that double room together.</p>
<p><img src="https://michelenasti.com/images/weber-pizza.JPG" alt="When he came to visit me, we did pizza in my pizza oven" /></p>
<p><em>That's Weber learning how to make pizza in Italy, around 2008.</em></p>
<p>Probably only at 21 you can think of renting a room for 6 months with an unknown person, and this could go bad in many ways, but luckily it turned so well we're close friends that also meet from time to time, even if we live in two different continents. He currently lives in Houston, Texas, where he works as a software consultant. He runs the blog <a href="https://www.dislexiavisual.net/">Dislexia Visual</a> (portuguese; English-translated version <a href="https://translate.google.com/translate?hl=&sl=pt&tl=en&u=https%3A%2F%2Fwww.dislexiavisual.net%2F">here</a>). At the time he managed to do several day jobs to pay his overseas studies, then go to party at night and hang out with friends: such a great determination!</p>
<p>When I got to know him better, he told me about the issues with his eyes: he already knew that in a couple of years he was going to be blind. He visited a moltitude of doctors, and all of them have told him the same thing, <strong>"you should have already been blind by now"</strong>. He went through many surgeries. Luckily, his body didn't surrender that easy and he still has a tiny fraction of sight.</p>
<p>With this interview I'd like to put some light on something that may happen to all of us, not only IT professionals. If even one of you will change the color pattern, or think about accessibility for a second, I'll have achieved my goal.</p>
<hr />
<h4>First question is an introduction to you: who is Weber, where did you grow up, study, work..</h4>
<p>I'm Weber Amaral, 36, and was born in southern Brazil. I lived in a small town in the country side my entire childhood. I studied Computer Science at UTFPR (Brazil) and when I was 21 y/o, I moved to Spain, where I spent a whole year in a student interchange program at the University of Seville and also had an internship in an automotive industry at the same city. Back to Brazil I moved to Sao Paulo (country's largest city) to work for Siemens. After 7 years there, I received an offer to move to Houston, Texas, where i've been living for the last 6 years with my amazing wife, Carol.</p>
<h4>What is the issue with your sight?</h4>
<p>When I was 6 y/o, I loved going to school. One day though, finding me too talkative, <strong>my kindergarten teacher sat me in the back of the classroom. She then noticed I couldn't see anything she put on the board.</strong> She called my parents, who took me to the eye doctor. We then found out I had only 5% of my sight in both eyes due to a vascular and autoimmune disease called <a href="https://en.wikipedia.org/wiki/Uveitis"><em>pars planitis</em></a>. Since then, I've gone through <strong>more than 50 surgeries</strong> and lost the sight on the right eye around 6 years ago. The left eye was doing its work "well" until it really start a new phase of degeneration almost 3 years ago. So, everyday for me now is a new discovery, since my vision is getting worse and worse.</p>
<h4>What is your daily job? How do you spend a typical day? Do you program?</h4>
<p>Since I graduated, in 2008, I've been working as a software consultant for different process industries, such as Oil & Gas, Chemicals, Petrochemicals, Steel Mill, Foods & Beverages, Pulp & Paper, etc. The 7 first years in Brazil, I performed multiple roles, which envolved coding, but also software deployment, requirements design and project management. In the last 6 years, in the US, I'm a software consultant of a specific Siemens tool, called XHQ. We design solutions for Operations Intelligence. Thus, my work involves requirements specification, solution design and software deployment. Coding nowadays for me is restricted to JavaScript, SQL, XSLT, PowerShell and a little bit of C#.</p>
<h4>What tech tools help you in your daily job?</h4>
<p>All my work environement is Windows, from client to servers. So, with the vision I have today, for now the accessibility tools contained inside the MIicrosoft OS have helped me to execute my daily job. I do have a <strong>big issue with light screens</strong>, so I use the Windows 10 dark mode (also for Office, Visual Studio and others). I also use extensions, such as <em>Dark Reader, Midnight Lizard</em> and others to transform regular websites in dark mode. Besides that, the magnifier (both PC and phone) are also very useful for me. I don't use PC voice command very often, but the Google Assistant is an everytime friend for me.</p>
<h4>Are there any subsides from the government, or the company?</h4>
<p>I am currently registered as a visually impaired in the Texas Workforce, a state agency responsible for supporting workers and employers in order to maintain a sustainable economic prosperity for all. In other words, they can help me to keep my job. Currently, the main benefit I get from them is the O&M training (<em>Orientation & Mobility training)</em>, which provides me with the tools and techniques to keep my independency despite of the vision degradation. On the same topic, my company acknowledges my situation and we work together in order to keep my productivity, while they provide me with all the required tools, devices and softwares.</p>
<h4>Do you feel discriminated by your colleagues?</h4>
<p>Not at all by my colleagues. I am very very blessed to work at the environment I currently do. Even <strong>when I don't feel comfortable to execute some activity due to my eye condition, the whole team is very supportive and we all work quickly to switch assignments.</strong> With that, I feel very empowered to tell my team what is OK or not for me to execute. But, i understand I am very lucky to be on that position and I'm pretty sure other people with disability might not fit in this scenario. All of this is related to my work environment, which again, is very positive for me. Nevertheless, there are other nuances when we discuss discrimination on the society as a whole, where, unfortunately, not always I feel as comfortable as I mentioned above.</p>
<h4>Do you think that you can change job in the future?</h4>
<p>I pretty much like what I do today and, most important, I feel very comfortable in the work environment I am right now. However, I definetely understand that <strong>the worse my vision gets, the more I have to adapt my carrer</strong>. That means that, at some point, the adaptations will be too significant and the changes will be more profound. So, answering the question, I don't plan or want to change job right now, but <strong>I also have no clue what comes next</strong>.</p>
<h4>What is your biggest fear?</h4>
<p>Well, my biggest fear is to live in a society which does not understand how difficult everything is for a person with disability. Unfortunately, there isn't much I can do to improve my sight and I am very conscient about future brings to me. Thus, my real fight is to make sure that people like me will always have good opportunities in life and do not suffer any type of discriminations.</p>
<p><em>Our interview ends here; feel free to ask more questions in the comments. And please, remember to pay a visit to</em> <a href="https://www.dislexiavisual.net/"><em>Dislexia Visual</em></a> <em>(portuguese; English-translated version</em> <a href="https://translate.google.com/translate?hl=&sl=pt&tl=en&u=https%3A%2F%2Fwww.dislexiavisual.net%2F"><em>here</em></a><em>).</em></p>
How to integrate a django-select2 component in Django Admin
2021-02-12T00:00:00Z
https://michelenasti.com/2021/02/12/how-to-integrate-a-django_select2-component-in-django-admin.html
<p>In a project we had a select box that will probably contain more than a hundred of results, and it will be very difficult for users to select the right one without typing. So we decided to implement a <a href="https://django-select2.readthedocs.io/en/latest/">django-select2</a> widget instead.</p>
<blockquote>
<p>I am not a Python expert, nor a Django or Django Admin expert. What you read here is what i learned along the way. It may be inaccurate or imprecise but I try to explain all the inner questions I got to myself.</p>
</blockquote>
<p>First of all, what is a <strong>Select2</strong> ? This one:</p>
<p><img src="https://michelenasti.com/images/select2.png" alt="a select2 component" /></p>
<p>What is <strong>Django</strong>? Django is a Python framework to build MVC websites.</p>
<p>And what is <strong>Django Admin</strong>? It is a way to build admin consoles for models in your domain. It supports users, groups, permissions, inserts, updates, lists, custom actions, etc. I think that alone it is worth the price of the whole stack. The major downside is that you end up configuring it instead of programming. And, you'll spend a lot of time in their documentation.</p>
<p>If you only have to add autocomplete on Model pages (like, the creation of an entity), <a href="https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields">probably autocomplete_fields is enough</a>. But if you need something more specialized, like a custom form, probably you need to add select2 by yourself. (No, I couldn't find a way to re-use the same select2 component that is in django admin, sorry.)</p>
<p>So, in order to integrate a select2 in django admin, here's the high level description of what to do:</p>
<h4>install django-select2</h4>
<p>I used pipenv so <code>pipenv install django-select2</code></p>
<h4>Add <code>django_select2</code> to <code>INSTALLED_APPS</code></h4>
<pre class="language-python"><code class="language-python"><span class="token comment"># settings.py</span><br />INSTALLED_APPS <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /> <span class="token string">"django_select2"</span><span class="token punctuation">,</span><br /><span class="token punctuation">]</span></code></pre>
<h4>Add django_select2 urls</h4>
<p>In the main urls.py, the same directory where settings.py is, add this line:</p>
<pre class="language-python"><code class="language-python"><span class="token comment"># urls.py</span><br />urlpatterns <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /> path<span class="token punctuation">(</span><span class="token string">"select2/"</span><span class="token punctuation">,</span> include<span class="token punctuation">(</span><span class="token string">"django_select2.urls"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /><span class="token punctuation">]</span></code></pre>
<p><strong>Why:</strong> it will be used by the dropdown to fetch the data.</p>
<h4>Specify a cache for the component</h4>
<p>Install and use a cache to get the data for the Select2 component. We were already using REDIS so here's our configuration.</p>
<p>First install django-redis:</p>
<pre class="language-bash"><code class="language-bash">pipenv <span class="token function">install</span> django-redis</code></pre>
<p>Then add the configuration:</p>
<pre class="language-python"><code class="language-python"><span class="token comment"># settings.py</span><br /><br />CACHES <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token string">"default"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><br /> <span class="token string">"BACKEND"</span><span class="token punctuation">:</span> <span class="token string">"django.core.cache.backends.locmem.LocMemCache"</span><span class="token punctuation">,</span><br /> <span class="token string">"LOCATION"</span><span class="token punctuation">:</span> <span class="token string">"unique-snowflake"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token string">"select2"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><br /> <span class="token string">"BACKEND"</span><span class="token punctuation">:</span> <span class="token string">"django_redis.cache.RedisCache"</span><span class="token punctuation">,</span><br /> <span class="token string">"LOCATION"</span><span class="token punctuation">:</span> <span class="token string">"redis://redis:6379/2"</span><span class="token punctuation">,</span><br /> <span class="token string">"OPTIONS"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><br /> <span class="token string">"CLIENT_CLASS"</span><span class="token punctuation">:</span> <span class="token string">"django_redis.client.DefaultClient"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><br /><br />SELECT2_CACHE_BACKEND <span class="token operator">=</span> <span class="token string">"select2"</span></code></pre>
<p><strong>Why</strong>: I only needed select2 cache, but Django then complains that there's no <code>default</code> cache, that's why I had to add the default one. You may want to use another kind of cache, see <a href="https://docs.djangoproject.com/en/3.1/topics/cache/">django docs</a>.</p>
<h4>Regarding JQuery</h4>
<p>Django Admin adds and uses JQuery, but it namespaces it under <code>django.JQuery</code>. This is problematic because the select2 javascript file expects jquery to be present in the global scope. So I will re-add it when defining the component. You'll see how in the next paragraph.</p>
<h4>Add the generic widget</h4>
<p>We've decided to create a generic widget to be inherited by all other select2 widgets that will be created over time.</p>
<p>So I created a new file called <code>form_components.py</code> containing:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">from</span> django_select2 <span class="token keyword">import</span> forms <span class="token keyword">as</span> s2forms<br /><br /><span class="token keyword">class</span> <span class="token class-name">BaseAutocompleteSelect</span><span class="token punctuation">(</span>s2forms<span class="token punctuation">.</span>ModelSelect2Widget<span class="token punctuation">)</span><span class="token punctuation">:</span><br /> <span class="token keyword">class</span> <span class="token class-name">Media</span><span class="token punctuation">:</span><br /> js <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">"admin/js/vendor/jquery/jquery.min.js"</span><span class="token punctuation">,</span><span class="token punctuation">)</span><br /><br /> <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token operator">**</span>kwargs<span class="token punctuation">)</span><span class="token punctuation">:</span><br /> <span class="token builtin">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__init__<span class="token punctuation">(</span>kwargs<span class="token punctuation">)</span><br /> self<span class="token punctuation">.</span>attrs <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">"style"</span><span class="token punctuation">:</span> <span class="token string">"width: 300px"</span><span class="token punctuation">}</span><br /><br /> <span class="token keyword">def</span> <span class="token function">build_attrs</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> base_attrs<span class="token punctuation">,</span> extra_attrs<span class="token operator">=</span><span class="token boolean">None</span><span class="token punctuation">)</span><span class="token punctuation">:</span><br /> base_attrs <span class="token operator">=</span> <span class="token builtin">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>build_attrs<span class="token punctuation">(</span>base_attrs<span class="token punctuation">,</span> extra_attrs<span class="token punctuation">)</span><br /> base_attrs<span class="token punctuation">.</span>update<span class="token punctuation">(</span><br /> <span class="token punctuation">{</span><span class="token string">"data-minimum-input-length"</span><span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token string">"data-placeholder"</span><span class="token punctuation">:</span> self<span class="token punctuation">.</span>empty_label<span class="token punctuation">}</span><br /> <span class="token punctuation">)</span><br /> <span class="token keyword">return</span> base_attrs</code></pre>
<p>You may see that the inner class <code>Media</code> readds the jquery as specified before.</p>
<p>In the constructor I specified a fixed width, feel free to adjust the style as you want.</p>
<p>In the <code>build_attrs</code> method we add all the select2 options you may want to override. The list of options, to write in kebab-case, is <a href="https://select2.org/configuration/options-api"><em>here</em></a>. The method <code>build_attrs</code> must return a dict of properties.</p>
<h4>Specialize the widget by sublcassing it</h4>
<p>Now we are ready to create a widget that subclasses our <code>BaseAutocompleteSelect</code> class. Here is an example:</p>
<pre class="language-python"><code class="language-python"><span class="token comment">## remember to add the imports!</span><br /><br /><span class="token keyword">class</span> <span class="token class-name">BookAutocompleteWidget</span><span class="token punctuation">(</span>BaseAutocompleteSelect<span class="token punctuation">)</span><span class="token punctuation">:</span><br /> empty_label <span class="token operator">=</span> <span class="token string">"-- select book --"</span><br /> search_fields <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">"name__icontains"</span><span class="token punctuation">,</span><span class="token punctuation">)</span><br /> queryset <span class="token operator">=</span> Book<span class="token punctuation">.</span>objects<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><br /> <span class="token builtin">type</span><span class="token operator">=</span><span class="token string">"Romance"</span><br /> <span class="token punctuation">)</span><span class="token punctuation">.</span>order_by<span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span></code></pre>
<ul>
<li>The empty_label contains the label that is shown when the dropdown is closed and no value is selected.</li>
<li><code>search_fields</code> is how the search has to be done. This depends on the model.</li>
<li><code>queryset</code> is the list of objects on whom the query is performed. It seems that django-select2 launches a warning if there's no "order_by" that's why I added one.</li>
</ul>
<h4>Use the widget in a form</h4>
<p>And now you can add the Widget in the form:</p>
<pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">BookForm</span><span class="token punctuation">(</span>forms<span class="token punctuation">.</span>Form<span class="token punctuation">)</span><span class="token punctuation">:</span> <br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><br /> book <span class="token operator">=</span> forms<span class="token punctuation">.</span>ModelChoiceField<span class="token punctuation">(</span><br /> label<span class="token operator">=</span><span class="token string">"Book: "</span><span class="token punctuation">,</span><br /> widget<span class="token operator">=</span>BookAutocompleteWidget<span class="token punctuation">,</span><br /> queryset<span class="token operator">=</span>Book<span class="token punctuation">.</span>objects<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><br /> <span class="token builtin">type</span><span class="token operator">=</span><span class="token string">"Romance"</span><br /> <span class="token punctuation">)</span><br /> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></code></pre>
<p>That's it!</p>
<h3>What confused me the most?</h3>
<p>First thing: how to use or specialize the method <code>ModelSelect2Widget.build_attrs</code> was a bit confusing for me. This is not described very well in documentation, at least for a non-python expert.</p>
<p>Second: Instead of using <code>forms.ModelChoiceField</code> i accidentally used <code>forms.ChoiceField</code> first. The missing <code>Model</code> prefix makes a substantial difference, infact the Form is not automatically valid when a value is selected. Also, the error I was getting was related to a missing property on another field. What a confusion! Had to debug django in order to understand what was going on. Anyway, if you want to go that way, you have to write custom code to validate the form.</p>
<p>In order to make the form automagically valid, I had to use the ModelChoiceField <em>and</em> specify the <code>queryset</code> again. This is because the ModelChoiceField verifies that the chosen element belongs to this queryset. This way it declares the form valid.</p>
"A quest'ora avresti dovuto essere cieco" - intervista a uno sviluppatore che sta perdendo la vista
2021-03-25T00:00:00Z
https://michelenasti.com/2021/03/25/you-should-be-blind-italiano.html
<p><strong>Hai mai pensato a come svolgeresti il tuo lavoro senza poter vedere?</strong> Lo devo ammettere, non avrei idea di dove iniziare, ma un mio caro amico lo sta sperimentando e penso sia il caso di condividere la sua storia.</p>
<p><a href="https://michelenasti.com/2021/01/28/no-sight-developer.html">(Questa post è una traduzione dell'articolo inglese che trovate qui)</a></p>
<blockquote>
<p>nota: il titolo originale dell'articolo era "saresti dovuto essere cieco". Sebbene questa sia la forma più diffusa in italiano, anche sui siti d'informazione, grammaticalmente è sbagliata. Per questo l'ho corretta in "avresti dovuto essere". <a href="https://www.treccani.it/magazine/lingua_italiana/articoli/scritto_e_parlato/Errata_Corrige.html">Qui la spiegazione sul perchè</a>, dal sito Treccani.</p>
</blockquote>
<p>Quando ho incontrato Weber nel 2006, entrambi eravamo studenti di informatica presso l'università di Siviglia, in Spagna. Io ero appena arrivato, non parlavo una parola di spagnolo, e cercavo una stanza da affittare. Entrambi stavamo visionando lo stesso appartamento e c'era una sola stanza disponibile, una doppia. Quindi, dopo 5 minuti, entrambi abbiamo deciso di prendere quella stanza insieme.</p>
<p><img src="https://michelenasti.com/images/weber-pizza.JPG" alt="" /></p>
<p><em>Questo è Weber che sta imparando a fare le pizze in Italia, nel 2008.</em></p>
<p>Forse solo a 21 anni si può pensare di affittare una stanza per 6 mesi con un perfetto sconosciuto, e questa cosa poteva andare male in molti modi, ma fortunatamente è andata così bene che siamo ancora amici. Ci siamo incontrati diverse volte anche se viviamo in due continenti diversi. In questo momento lui vive a Houston, in Texas, dove lavora come consulente software. Scrive sul suo blog <a href="https://translate.google.com/translate?hl=&sl=pt&tl=it&u=https%3A%2F%2Fwww.dislexiavisual.net%2F"><strong>Dislessia Visiva</strong></a> (l'originale è in portoghese, ma vi ho linkato la versione italiana). In Spagna riusciva a fare diversi lavori per pagarsi i suoi studi oltreoceano, poi la sera veniva alle feste e usciva con gli amici: che determinazione!</p>
<p>Quando l'ho conosciuto meglio mi ha parlato del problema coi suoi occhi: lui già sapeva che in un paio d'anni sarebbe diventato cieco. Ha visto una moltitudine di dottori, e tutti gli hanno detto la stessa cosa, <strong>"dovresti essere già cieco a quest'ora"</strong>. Ha fatto tantissimi interventi. Fortunatamente, il suo corpo non si è arreso così facilmente ed ha ancora una piccola frazione della sua vista.</p>
<p>Con questa intervista voglio portare alla luce qualcosa che potrebbe accadere ad ognuno di noi, non solo professionisti dell'IT. Se solo uno di noi cambierà i colori del suo sito, o penserà all'accessibilità per un secondo, avrò conseguito il mio piccolo scopo.</p>
<hr />
<h4>La prima domanda è una tua presentazione: chi è Weber, dove sei cresciuto, dove hai studiato, quali sono le tue esperienze lavorative...</h4>
<p>Mi chiamo Weber Amaral, ho 36 anni, e sono nato nel sud del Brasile. Ho vissuto in una piccola cittadina nella campagna per tutta la mia infanzia. Ho studiato Informatica alla UTFPR (Brasile) e a 21 anni mi sono trasferito in Spagna, dove ho trascorso un intero anno in un programma di interscambio studentesco all'università di Siviglia; ho anche fatto uno stage presso un'industria automobilistica di quella città. Tornato in Brasile mi sono trasferito a San Paolo (la città più grande del Brasile) per lavorare per Siemens. Dopo 7 anni lì, ho ricevuto un'offerta per trasferirmi a Houston, Texas, dove vivo da 6 anni con mia moglie Carol.</p>
<h4>Qual è il tuo problema con la vista?</h4>
<p>A 6 anni amavo andare a scuola. Un giorno, <strong>siccome ero troppo chiacchierone, la maestra mi ha spostato di posto in fondo alla classe. Così ha notato che non riuscivo a vedere niente di quello che c'era sulla lavagna.</strong> Ha chiamato i miei genitori che mi hanno portato da un oculista. Abbiamo così scoperto che avevo solo il 5% della vista in entrambi i miei occhi a causa di una malattia vascolare autoimmune chiamata <a href="https://it.wikipedia.org/wiki/Uveite"><em>pars planitis</em></a> _(_più nota come <em>uveite</em> in italiano). Da allora ho fatto <strong>più di 50 operazioni</strong> e ho perso totalmente la vista all'occhio destro 6 anni fa. Il sinistro continuava a funzionare "bene" fino a 3 anni fa, quando una nuova fase di degenerazione è iniziata. Quindi, ogni giorno per me è una nuova scoperta, visto che la mia vista diventa sempre peggio.</p>
<h4>Cosa fai a lavoro? Come passi una giornata tipo? Riesci a programmare?</h4>
<p>Da quando mi sono laureato nel 2008 ho lavorato come consulente software per diverse industrie, come quelle petrolifere, chimiche, acciaierie, cibo & bevande, cellulosa, etc. I primi 7 anni in Brasile ho svolto diversi ruoli tra cui sviluppatore ma anche devops, analisi dei requisiti e project management. Negli ultimi 6 anni, negli US, sono un consulente software per uno specifico tool della Siemens chiamato XHQ. Scriviamo soluzioni per Operations Intelligence. Quindi, il mio lavoro è fatto di raccolta di specifiche, design di soluzioni e anche di rilasci software. Posso scrivere codice limitatamente a Javascript, SQL, XSLT, PowerShell e un po' di C#.</p>
<h4>Quali strumenti usi nel tuo quotidiano?</h4>
<p>Il mio ambiente di lavoro è Windows, dai client ai server. Quindi, con la visione che ho ora, gli strumenti di accessibilità contenuti nel sistema operativo di Microsoft mi aiutano a eseguire i miei compiti giornalieri. Ho un <strong>grande problema con gli schermi chiari</strong>, quindi uso Windows 10 in modalità scura (stesso discorso per Office, Visual Studio e gli altri). Inoltre uso alcune estensioni come <em>Dark Reader, Midnight Lizard</em> e altre per trasformare i siti web in dark mode. Oltre a questi, trovo molto utile la lente di ingrandimento (sia su PC che su telefonino). Non uso molto i comandi vocali su PC, mentre l'assistente vocale di Google è un amico quotidiano.</p>
<h4>Ci sono dei sussidi da parte del governo, o dalla tua azienda?</h4>
<p>In questo momento sono registrato come ipovedente presso l'ufficio del lavoro del Texas, un'agenzia statale che ha la finalità di aiutare i lavoratori e i datori di lavoro. In altre parole, quest'ufficio mi può aiutare a mantenere il mio lavoro. Attualmente il principale beneficio che ricevo da loro è l'<em>O&M Training</em> (<em>Orientation & Mobility training,</em> ossia "Allenamento all'orientamento e alla mobilità") che mi fornisce tecniche e strumenti per avere un po' di indipendenza nonostante il degradarsi della vista. Sullo stesso piano: la mia azienda supporta la mia situazione e lavoriamo insieme in modo da far rimanere costante la mia produttività, infatti loro mi forniscono strumenti, dispositivi e software necessari.</p>
<h4>Ti senti discriminato dai tuoi colleghi?</h4>
<p>Non dai miei colleghi. Sono molto fortunato a lavorare nell'ambiente in cui mi trovo ora. <strong>Anche quando non mi sento a mio agio a fare un'attività a causa della mia vista, il team mi supporta e ci scambiamo velocemente le attività.</strong> Mi sento fortunato a poter dire al mio team che cosa è OK per me e cosa non lo è. Tuttavia, capisco che sono molto fortunato e sono molto sicuro che altre persone con disabilità potrebbero non rientrare in questo scenario. Tutto questo discorso vale per il mio ambiente di lavoro che, come detto, è molto positivo per me. Se parliamo di discriminazione della società nella sua interezza, ci sono altre sfumature in cui, purtroppo, non mi sento sempre così a mio agio.</p>
<h4>Pensi che potrai cambiare lavoro in futuro?</h4>
<p>Mi piace ciò che faccio oggi e, cosa più importante, mi sento bene nell'ambiente di lavoro in cui mi trovo ora. Tuttavia, capisco perfettamente che <strong>più peggiora la mia vista, più dovrò adattare il mio lavoro</strong>. Questo significa che a un certo punto gli adattamenti saranno più significativi e i cambiamenti più profondi. Quindi per rispondere alla domanda, non ho intenzione di cambiare lavoro in questo momento, ma <strong>non ho idea di cosa potrebbe venire dopo</strong>.</p>
<h4>Qual è la tua paura più grande?</h4>
<p>Beh, la mia paura più grande è di vivere in una società che non capisce quanto tutto sia difficile per una persona con una disabilità. Sfortunatamente, non c'è molto che io possa fare per migliorare la mia vista e sono cosciente di ciò che il futuro mi porterà. Tuttavia, la mia vera sfida è fare in modo che persone come me avranno sempre buone opportunità nella vita e che non soffriranno alcun tipo di discriminazione.</p>
<p><em>La nostra intervista finisce qui; se vuoi puoi lasciare altre domande nei commenti. E per favore, fai un giro su</em> <a href="https://translate.google.com/translate?hl=&sl=pt&tl=it&u=https%3A%2F%2Fwww.dislexiavisual.net%2F"><em>Dislexia Visual</em></a> <em>(tradotto in automatico da Google in italiano).</em></p>
Clickhouse: a Database for your next Big Data project
2021-04-02T00:00:00Z
https://michelenasti.com/2021/04/02/clickhouse-a-database-for-your-next-big-data-project.html
<p>When I started this blog I would have never guessed that I would write a post about ... Big Data?</p>
<p>That's not a field where I am specialized, but since at work we had a problem, we also needed a solution. Let's dig into this!</p>
<h3>The problem: a lot of data and a fragile cluster</h3>
<p>We are processing a lot of streaming data coming from all over the web and we are using an ElasticSearch cluster for this. ElasticSearch is a great technology, very mature, supports many different use cases, but it has some issues: it costs a lot to run on very perfomant hardware, and due to the nature of our input data queries can bring down the whole cluster, making it very fragile. I can't enumerate the times the team had to fix the cluster by restarting services even very late at night.</p>
<p>Even if after some time we figured out what was taking down the ES cluster, we still think it is a very costly option for what we need. So we started exploring other approaches and we stumbled on...</p>
<h3>The alternative: Clickhouse Database</h3>
<p>Clickhouse is an <strong>OLAP</strong> database. OLAP means <em>On Line Analytical Processing</em>, and it's a synonime for "big data" nowadays. Think of these databases as those that are fast on complex search queries, where data may be huge.</p>
<p>The standard, relational, general purpose databases are called <strong>OLTP</strong>, that is <em>On Line Transactional Processing</em>. The <em>Transactional</em> here makes a lot of difference, in fact those databases try to preserve data integrity over speed. In this category you may see MySQL, Postgres, etc.</p>
<p>So why <strong>Clickhouse</strong> was a surprise? Well, for two factors mainly. First, it uses SQL to query data, so we don't have to learn a new query language.</p>
<p>Second, <strong>they changed just <em>one</em> thing - the way the data is stored on disk - and this changed everything else</strong>.</p>
<p>In traditional relational databases, rows are stored on disk continously. Imagine a <code>Users</code> table. On disk, you'll see that every block contains all the data of a row, like this:</p>
<p>0: <id0> | <name0> | <surname0> | <email0> | <password0>
1: <id1> | <name1> | <surname1> | <email1> | <password1>
...</p>
<p>In Clickhouse, data is stored in a <em>columnar</em> way, so the same data is arranged on disk differently:</p>
<p><id0> | <id1> | ... | <idN> |
<name0> | <name1> | ... | <nameN>
...
<password0> | <password1> | ... | <passwordN></p>
<p>Data of the same column is stored continuously.</p>
<p>This has two big implications:</p>
<ul>
<li><strong>adding new data is not performant</strong>. Clickhouses prefers large batches of data instead of sending one object at a time. Data insertion requires more time and data is eventually consistent.</li>
<li><strong>querying can be much faster</strong>. Disks are phisically optimized to read continuous data, and since data is stored continously, doing aggregations or scans over the table is dramatically faster.</li>
</ul>
<p>Another funny thing is that we don't need a whole cluster: just one server, at the moment, is handling the same amount of data that we were storing in a cluster of 8 ES machines. Of course we're not crazy horses and we'll probably set up a replica, don't worry :) But still 2 is less than 8.</p>
<p>So what are the cons?</p>
<ul>
<li><strong>this is not a database for every kind of project</strong>. This will not substitute your redis, mysql, or mongo. This is another tool in your toolbox that you may need if you have to do complex queries on big data.</li>
<li><strong>This is not a complete replacement for ElasticSearch</strong>. ES can do much more and I admit my knowledge of both is very superficial. Be sure to choose the right one for your next project!</li>
</ul>
<p>I am still in the first stage of meeting this DB so don't ask complex questions, I may not have the answers yet. Probably I will also not have the real chance to get my hands dirty because of my role in my team (frontendish fullstackish guy). Let me know if you use it and what are your impressions!</p>
Want to play songs in your web app? Say no more with JS Headless Playlist Player
2021-05-03T00:00:00Z
https://michelenasti.com/js-headless-playlist-player/
<p>Welcome to my latest project: <a href="https://github.com/musikele/js-headless-playlist-player"><strong>JS Headless Playlist Player</strong></a>.</p>
<p>As the name suggests, it's a Javascript API to build your own web player. You write the graphics, I give you the free JS glue, and let the music play!</p>
<h2>Wait, what's that</h2>
<p>One of my friends is building a social web app to create music and to listen other member's creations. Very cool project, but he wanted a way to play the music from his community sequentially. Of course there's SoundCloud, but we wanted to implement this thing internally. Also, I didn't want to provide graphics because every future consumer will be creating a UI that respects it's own tastes.</p>
<p>For example, you can build your own Winamp, lol</p>
<p><img src="https://michelenasti.com/images/winamp.png" alt="" /></p>
<p><em>(If you want to see a real Winamp web clone, go to</em> <a href="https://webamp.org/"><em>webamp.org</em></a> <em>- NOTE: it doesn't use my library under the hood)</em></p>
<h2>How does JS Headless Playlist Player work?</h2>
<p>You compile the project and add the script to your html and then you have this global object, called <code>PlaylistPlayer</code>, that implements a very nice API:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// add songs to the library.</span><br /><span class="token comment">// The only mandatory attribute is `url`. But you can also pass names or other</span><br /><span class="token comment">// info you may need to build your player.</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'firstsong.mp3'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'secondsong.wav'</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// starts playing songs sequentially</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">play</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// pauses the song; it can later be resumed by calling `.play()`.</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// will stop and go to the start of the *current* song.</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// go to next song if present</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// go to prev song if present</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">prev</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// returns the current song list</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">getSongsList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// get current playing song index</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">getCurrentSongIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// get player state</span><br />PlaylistPlayer<span class="token punctuation">.</span><span class="token function">getCurrentState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token comment">// output: [</span><br /><span class="token comment">// 'unloaded', // when no song has been loaded.</span><br /><span class="token comment">// 'stopped', // songs are loaded but no song is playing. If you press play song will start from start.</span><br /><span class="token comment">// 'playing', // music coming out of the speakers!</span><br /><span class="token comment">// 'paused' // song is paused; if you press play it will resume from last paused location.</span><br /><span class="token comment">// ]</span><br /><br /><span class="token comment">// To listen for changes in the PlaylistPlayer state changes, you can</span><br /><span class="token comment">// attach a listener to 'playlistEvent' event:</span><br />document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'playlistEvent'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">evt</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>evt<span class="token punctuation">.</span>detail<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token comment">// {</span><br /> <span class="token comment">// currentState: "playing",</span><br /> <span class="token comment">// songs: [...],</span><br /> <span class="token comment">// currentSongIndex: 2</span><br /> <span class="token comment">// }</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2>Under the hood: XState</h2>
<p>To build this small project I decided to go with a state library called <a href="https://xstate.js.org/">XState</a>. <strong>XState</strong> is a library to define state machines very easily. Audio players are simple finite state machines.</p>
<p>This library was nothing more than an exercise to grasp the fundamentals of XState, but I must say I didn't get all it has to offer. Sometimes there are more ways to do the same thing, and still I don't know if some of what I had in mind could be done.</p>
<p>To give you more detail: The State Machine has an internal state, that lives inside a <code>context</code> object. In this context I store the songs array, what song I am currently playing, and all the things I need to run the lib.</p>
<p>Then, when you jump from a state to another (e.g. from <code>stopped</code> to <code>playing</code>), you can trigger a function for example. In XState, those functions are called <strong>actions</strong>.</p>
<p>However, actions are thought for immediate-returning actions; in my case the "play" action was long-lasting. For these scenarios, XState offers the concept of <strong>activity</strong>, that is a perpetual function. When you exit from this state, XState will automatically shut down your activity by calling the exit function you defined (that in my case will stop the audio).</p>
<p>There is much more than this. I honestly tried to hack the library as fast as possible, so I cannot consider myself an expert.</p>
<h2>Why HTML5 Audio instead of WebAudio?</h2>
<p>This project's goal is to go in the browser, and browsers have excellent support for the Audio object. It is the easiest API out there to play songs. I didn't want to overcomplicate things. Let me know if this is a limitation for you!</p>
<p>Enough said, let me know if you find any bugs (there are many!) and if you have feature requests!</p>
1:1 Meetings
2021-06-29T00:00:00Z
https://michelenasti.com/one-to-one-meetings/
<p>Do you want to know what's the most important meeting, the one none of us has ever done?<br />
It's <strong>1:1 meeting</strong>.</p>
<p><em>(If you've never heard of this meeting don't feel ashamed, i discovered it last year!)</em></p>
<p><strong>The 1:1 meeting</strong> (pronunced one-to-one) <strong>happens regularly</strong> (for example, every 14 days) <strong>with your manager</strong>. It's an <em>intimate</em> meeting where you can discuss about everything. In this meeting, some managers may talk about work-related things only marginally, because there are other moments to do that. Some topics you can touch during this meeting are:</p>
<ul>
<li>how are you</li>
<li>your team</li>
<li>your family</li>
<li>the company</li>
<li>our market, customers, stakeholders...</li>
<li>career, growth</li>
<li>...</li>
</ul>
<p><img src="https://michelenasti.com/images/pexels-mentatdgt-1311518.jpg" alt="two people having a meeting" /></p>
<p>It sounds crazy, expecially if you're italian, that <strong>for an hour you can talk about human things among human beings</strong>! Usually those meetings last between 30 and 60 minutes and it seems to be in the <em>Big Brother</em> (tv show) confession room.</p>
<p>Another beautiful thing: <strong>during this hour you can ask your boss the same questions</strong>, and sometimes it's nice to see he's having the same struggles you have, even though on different levels.</p>
<p><strong>During the years I worked for italian companies the 1:1 meeting has never happened</strong>, if not occasionally. Indeed, in US companies the 1:1 meeting is scheduled every two weeks and, for what I understand, it's a common practice for big companies. I use it to ask my boss how's doing, what he's working on because his work is usually reflected on us after one-two months, so I've got some time to start studying them.</p>
<p>One note, by the way: <strong>not everybody loves this genre of meetings</strong>. I think they're fundamental to streighten the bond between the team member and the manager, and a side effect is that the whole team beneficts from it. Others may think it's a waste of time. So, if the manager itself doesn't <em>believe</em> in the power of those meetings, those meetings will loose their benefits.</p>
<p>To conclude, <strong>my judgement is positive</strong> and I wish you can organize the same policy in the companies you'll lead.</p>
Remotalypse NOW: non tutti vogliono tornare in ufficio
2021-07-06T00:00:00Z
https://michelenasti.com/remotalipse-now-non-tutti-vogliono-tornare-in-ufficio/
<p><em>Questo è un messaggio che ho mandato in mailing list</em> <a href="https://www.meetup.com/it-IT/devday-salerno/"><em>DevDay Salerno</em></a><em>, a cui vi consiglio di iscrivervi se siete della zona.</em></p>
<p>Dopo un anno e mezzo di pandemia e di remote working, molte aziende si stanno riorganizzando per ritornare in ufficio a settembre 2021. Tuttavia, dopo aver assaporato la libertà di poter lavorare da casa, molti dipendenti non vogliono più tornare a occupare le vecchie scrivanie. In tanti si sono chiesti se andare fisicamente in un posto, la mattina, fosse davvero indispensabile per poter svolgere i propri compiti. Soprattutto, molti hanno riscoperto che la vita è molto più dell’ufficio, potendo assistere alla crescita dei propri figli o praticando hobby e sport che altrimenti non avrebbero potuto praticare. Molti altri si sono trasferiti da San Francisco (dove la vita è carissima) alla loro città natale, toccando con mano quanti soldi vanno letteralmente in fumo per affitto e servizi che in altre parti del mondo costano meno della metà.</p>
<p>Risultato? <strong>Si stima che tra il 10 e il 25% dei dipendenti USA si licenzierà e non ritornerà a lavoro</strong>.</p>
<p><img src="https://michelenasti.com/images/pexels-jill-wellington-39853.jpg" alt="" /></p>
<p><em>Una classica foto dei remote workers dopo essersi licenziati.</em></p>
<p>Un numero così alto di dipendenti che si licenzia potrebbe mandare KO un'azienda. Non pensate al mercato italiano, dove bene o male si lavora per guadagnare uno stipendio che solitamente è di quattro spiccioli. Negli USA la situazione è molto più dinamica. Le aziende tecnologiche pagano bene e il mercato interno è in perenne carenza di personale. <strong>Un -10% nella forza lavoro di Google, che ha 135k dipendenti, sono 13k persone che non vanno più a lavorare.</strong> L’equivalente degli abitanti del mio comune!</p>
<p>L’impressione è che <strong>il processo di trasformazione da lavoro in ufficio verso lavoro da remoto fosse già iniziata da tempo</strong>, tuttavia il tasso di crescita era lento e stabile. La pandemia ha cambiato tutto, e d’un tratto ci siamo trovati nel 2025 (o nel 2030?), solo che alcune aziende non l’hanno capito.</p>
<p><strong>Ma le aziende dovrebbero fare i propri interessi o quelli dei loro dipendenti?</strong> Le statistiche dicono che dove ci sono dipendenti felici c’è anche un grande fatturato, ma forse è più vero il contrario. Le aziende che vogliono tornare in ufficio credono fermamente che solo in ufficio si crei quell’alchimia che porta le persone a collaborare, ad essere empatiche, a pensare ai problemi in maniera nuova e sviluppare nuove soluzioni di gruppo. Nella loro visione, Il remote è l’elogio dell’individualismo, l’ufficio la patria della collaborazione.</p>
<p>Dunque, succederà? Davvero ci saranno licenziamenti di massa? O sono solo i giornali che provano a costruire una storia lì dove in realtà non c’è nulla di concreto? Staremo a vedere. Intanto, mi sorprende che in Italia nessuno ci stia pensando seriamente.</p>
<p>Caro lettore, è il momento di tornare a studiare <strong>l'inglese</strong>. Non so come andrà a finire, ma il trend è abbastanza chiaro, e nei prossimi 5-10 anni il lavoro come lo intendiamo oggi non sarà più lo stesso. Sappiamo tutti che un framework si può imparare in 1 mese, mentre l'inglese ha bisogno di pratica ed allenamento costante. Chi lo parla e lo scrive può ambire al mondo intero, chi invece non lo parla resta bloccato nei confini nazionali con tutte le sue contraddizioni.</p>
<p>Fonti:</p>
<ul>
<li><a href="https://www.psychnewsdaily.com/a-third-of-wfh-employees-say-theyd-rather-quit-than-return-to-full-time-office-work/">34% of WFH (work from home) workers say they’d rather quit than return to full-time office work</a></li>
<li><a href="https://fortune.com/2020/12/06/offices-covid-workers-returning-never-want-to-stats-data-2/">Nearly a third of workers don’t want to ever return to the office</a></li>
<li><a href="https://www.axios.com/resignations-companies-e279fcfc-c8e7-4955-8a9b-47562490ee55.html">"Great resignation" wave coming for companies</a></li>
</ul>
Move your photos from Google Photos to Synology without losing your mind
2021-07-19T00:00:00Z
https://michelenasti.com/from-google-photos-to-synology-photos/
<p>This guide is for you if:</p>
<ul>
<li>you care about the privacy of your photos</li>
<li>you don't want to pay Google</li>
<li>you own a Synology NAS.</li>
</ul>
<p>Synology offers an alternative called Synology Photos that works "well". To be precise, on Synology <strong>DSM</strong> (the operating system) 6.* the photos app was called <strong>Synology Moments</strong>, while on version 7.* it's called <strong>Synology Photos</strong>. My article will try to cover both, but since I migrated to version 7, this is the primary target of my guide.</p>
<p>Here's a rough guide on the process.</p>
<ul>
<li>Go on Google Takeout - the unified service to download all your data from google servers - and select only the photo albums you want to download.</li>
<li>Take the download link and download it on your NAS. You'll need a browser extension for this. I use <a href="https://addons.mozilla.org/it/firefox/addon/cliget/">cliget</a> for Firefox.</li>
<li>SSH to your nas and extract the archives.</li>
<li>Rsync photos in the right folder.</li>
</ul>
<p>Scary, of all those steps? It took me months to perfect this procedure, but now I am quite happy with it and I have almost completed the migration. In the end, it's not that difficult but it will require time. Be prepared.</p>
<h2>Download backup from Google Takeout</h2>
<p>This step is mostly point-n-click.</p>
<ul>
<li>Go on <a href="https://takeout.google.com/">takeout.google.com</a> and log in</li>
<li>select only google photos.</li>
<li>Then, <em>wait a lot</em> (20-30 seconds?) until the button <em>All photo albums</em> appears.</li>
</ul>
<p><img src="https://michelenasti.com/images/schermata-2021-07-19-alle-11-39-09.png" alt="" /></p>
<ul>
<li>Click on <em>deselect all</em> again and select only albums <em>Photos from XXXX</em> that you are interested to. These are albums that will contain all photos for every year; I assume other albums contain duplicates of these photos, that's why I deselected all the others.</li>
</ul>
<blockquote>
<p>tip: you can select only one year to do a test before selecting all years.</p>
</blockquote>
<p><img src="https://michelenasti.com/images/schermata-2021-07-19-alle-11-37-45.png" alt="" /></p>
<ul>
<li>Go to step two. Select <em>Email me a link to downlad</em> and select <em>50 GB</em> archive size. We will download the archive on the NAS directly, without passing from our local pc.</li>
<li>Click on <em>create export</em> and wait a couple of hours. You'll receive an email from Google when this is done.</li>
</ul>
<h2>Download the files directly on the NAS</h2>
<p>For this, you'll require:</p>
<ul>
<li>ssh access on the NAS</li>
<li>a browser extension that allows cookies in download link. I use <a href="https://addons.mozilla.org/it/firefox/addon/cliget/">cliget</a> for Firefox. Basically, this extension will translate the download link to a <code>curl</code> string. <code>curl</code> is a command that allows to make http requests with a lot of options; in our case, it will attach google cookies so Google will believe that we are logged users that are doing the download. I don't use Chrome so I hope somebody will suggest a similar extension for Chrome in the comments.</li>
</ul>
<p>So, back to the procedure.</p>
<p>Google will send you a link to download your files. Once you click on "download", Google will ask you to log in again and then the browser will prompt to select a location where to download the file. <strong>Click cancel here.</strong> As i said, we're not going to download the files on the pc, but rather we're going to get the <code>curl</code> string to download the file on the NAS.</p>
<p>So, once you've clicked "cancel", click on cliget extension:</p>
<p><img src="https://michelenasti.com/images/schermata-2021-07-19-alle-11-45-19.png" alt="" /></p>
<p>And now, click on the export file, you'll get a curl executable string:</p>
<p><img src="https://michelenasti.com/images/schermata-2021-07-19-alle-11-46-21.png" alt="" /></p>
<p>Copy this very long, preselected string and open an ssh connection to your Nas:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">ssh</span> <span class="token operator"><</span>nas-address<span class="token operator">></span><br /> <span class="token comment"># from now on, if connection succeded, we are operating on the NAS</span><br />$ <span class="token function">mkdir</span> google-takeout<br />$ <span class="token builtin class-name">cd</span> google-takeout<br /> <span class="token comment"># paste the very long curl string here followed by `&` </span><br />$ <span class="token function">curl</span> <span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span>. <span class="token parameter variable">--output</span> <span class="token string">'takeout-20210718T158137Z-001.zip'</span> <span class="token operator">&</span> </code></pre>
<p>Note the <code>&</code> at the end. If you're not familiar with unix, this means "start this process in the background". At this point you'll see that the terminal will start to become a little bit messy, with curl output breaking text. However, type <code>exit</code> and hit Enter. You'll exit the ssh connection while the <code>curl</code> operation will still be in place.</p>
<p>Depending on how big are your archives, this can take a lot of time. Do this for every file google has created.</p>
<p>You can re-enter ssh and check if the file is still downloading by doing a couple of <code>ls -al</code> and check the files size, if its getting bigger and bigger, this means that the download is in progress.</p>
<h2>Extract archives</h2>
<p>I am honest with you, I do this step with UI, but since we are on a terminal you could also ssh on to the nas and launch</p>
<pre class="language-bash"><code class="language-bash">$ 7z x takeout<span class="token punctuation">..</span><span class="token punctuation">..</span>zip </code></pre>
<p>and wait. This will take some time...</p>
<p>The archive contains photos and json files that contain some extra properties on those photos. So, for example you may find file <code>IMG00132.jpg</code> and file <code>IMG00132.jpg.json</code>.</p>
<p>In <code>.jpg</code> files you'll find all the original metadata, like GPS coordinates, dates, etc. This is true for all photos, except those created by Google Photos like GIFs, videos, and collages.</p>
<p>So what's in json files? There are some more metadata that is inferred by google photos, here's an example:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"IMG_0349.JPG"</span><span class="token punctuation">,</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span><br /> <span class="token property">"imageViews"</span><span class="token operator">:</span> <span class="token string">"20"</span><span class="token punctuation">,</span><br /> <span class="token property">"creationTime"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token string">"1464882293"</span><span class="token punctuation">,</span><br /> <span class="token property">"formatted"</span><span class="token operator">:</span> <span class="token string">"2 giu 2016, 15:44:53 UTC"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"photoTakenTime"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token string">"1464873329"</span><span class="token punctuation">,</span><br /> <span class="token property">"formatted"</span><span class="token operator">:</span> <span class="token string">"2 giu 2016, 13:15:29 UTC"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"geoData"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"latitude"</span><span class="token operator">:</span> <span class="token number">46.62</span>...<span class="token punctuation">,</span><br /> <span class="token property">"longitude"</span><span class="token operator">:</span> <span class="token number">8.58</span>...<span class="token punctuation">,</span><br /> <span class="token property">"altitude"</span><span class="token operator">:</span> <span class="token number">1445.117</span>...<span class="token punctuation">,</span><br /> <span class="token property">"latitudeSpan"</span><span class="token operator">:</span> <span class="token number">0.0</span><span class="token punctuation">,</span><br /> <span class="token property">"longitudeSpan"</span><span class="token operator">:</span> <span class="token number">0.0</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"geoDataExif"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"latitude"</span><span class="token operator">:</span> <span class="token number">46.62</span>...<span class="token punctuation">,</span><br /> <span class="token property">"longitude"</span><span class="token operator">:</span> <span class="token number">8.58</span>...<span class="token punctuation">,</span><br /> <span class="token property">"altitude"</span><span class="token operator">:</span> <span class="token number">1445.1176</span>...<span class="token punctuation">,</span><br /> <span class="token property">"latitudeSpan"</span><span class="token operator">:</span> <span class="token number">0.0</span><span class="token punctuation">,</span><br /> <span class="token property">"longitudeSpan"</span><span class="token operator">:</span> <span class="token number">0.0</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"url"</span><span class="token operator">:</span> <span class="token string">"https://lh3.googleusercontent.com/vwjRf..."</span><span class="token punctuation">,</span><br /> <span class="token property">"googlePhotosOrigin"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"fromSharedAlbum"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>If you read online, for example in the documentation of <a href="https://github.com/mattwilson1024/google-photos-exif#structure-of-google-takeout-export">projects like this</a>, you may think that <code>photoTakenTime</code> is not preserved. Indeed, I did a few checks and for all original photos it is preserved. Only the created photos (gifs, videos, collages) will not have the right exif attributes and those will show up with today's date. So, you may want to use the linked tool to sanitize EXIF data on your photos. I didn't.</p>
<h2>Copy files to Synology Photos dir</h2>
<p>And now the last part of this tutorial.</p>
<p>Once you have extracted the archive this is the folder structure:</p>
<p>google-takeout
|
|-- Takeout
|
|-- Google Photos
|
|-- Photos from 2015
|-- Photos from 2016
|-- ...</p>
<ul>
<li><code>google-takeout</code> is the folder I suggested to create, where we're going to operate.</li>
<li><code>Google Photos</code> may be actually translated by Google. In my case (italian), it's <code>Google Foto</code>.</li>
</ul>
<p>Now, we need to understand <em>where</em> we want to put these photos. As I said, I was coming from DSM 6.* and later I moved to DSM 7.*.</p>
<ul>
<li>For DSM 6.*, Synology Moments used to store photos in <code>~/Drive/Moments/</code></li>
<li>DSM 7.* Synology Photos is storing <code>~/Photos/</code> . Also, if you did the migration, your Moments photos are in <code>~/Photos/Moments</code>.</li>
</ul>
<p>I think we are ready for the copy. To do that I prefer to use <code>rsync</code> for its simplicity. Here's the command I use. Launch it from <code>~</code>:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">rsync</span> <span class="token punctuation">\</span><br /><span class="token parameter variable">-avr</span> <span class="token punctuation">\</span><br />--ignore-existing <span class="token punctuation">\</span><br />--dry-run <span class="token punctuation">\</span><br /><span class="token parameter variable">--exclude</span> <span class="token string">'*.json'</span> <span class="token punctuation">\</span><br /><span class="token parameter variable">--stats</span> <span class="token punctuation">\</span><br /><span class="token parameter variable">--progress</span> <span class="token punctuation">\</span><br />./google-takeout/Takeout/Google<span class="token punctuation">\</span> Foto/Photos<span class="token punctuation">\</span> from<span class="token punctuation">\</span> <span class="token number">2016</span>/ <span class="token punctuation">\</span><br />Photos/Moments/2016/</code></pre>
<ul>
<li><code>-avr</code> means "preserve <strong>a</strong>ll unix attributes, <strong>v</strong>erbose, <strong>r</strong>ecursive"</li>
<li><code>--ignore-existing</code> is to avoid to overwrite files that are already there. This way you can also relaunch the process multiple times if anything goes wrong.</li>
<li><code>--dry-run</code> is only used for <strong>testing</strong>. Basically, it executes the rsync command but will not write to disk, so it's good to see what will happen when the command is launched for real.</li>
<li><code>--exclude '*.json'</code> to avoid copying json files.</li>
<li><code>--stats</code> to see nice stats at the end.</li>
<li><code>--progress</code> to get a nice progress status</li>
<li>The last two lines are the <strong>source</strong> folder and the <strong>destination</strong> folder. <strong><em>Note:</em></strong> <em>the Google Foto folder can have a different name if you are using Google in another language!!!</em></li>
</ul>
<p>You may prefer the command on one line:</p>
<pre class="language-bash"><code class="language-bash">$ <span class="token function">rsync</span> <span class="token parameter variable">-avr</span> --ignore-existing --dry-run <span class="token parameter variable">--exclude</span> <span class="token string">'*.json'</span> <span class="token parameter variable">--stats</span> <span class="token parameter variable">--progress</span> ./google-takeout/Takeout/Google<span class="token punctuation">\</span> Foto/Photos<span class="token punctuation">\</span> from<span class="token punctuation">\</span> <span class="token number">2016</span>/ Photos/Moments/2016/</code></pre>
<p>Again, <strong>remember to remove option --dry-run to actually copy files</strong> otherwise the command will have no effect.</p>
<p>As you can see I prefer to organize folders based on years. You are not forced to do so.</p>
<p>You'll have to launch this command for every Google Photo album that you have.</p>
<h2>Conclusion</h2>
<p>This was a huge jurney. It took me some months to figure out, some days to actually find the will to do it, and a couple of hours per every year that I transferred. I hope this article will help someone because all the info I found on the web were very sparse and this is probably the first article that contains all the journey from start to end.</p>
<p>Enjoy your photos!</p>
A better Git Log
2021-09-17T00:00:00Z
https://michelenasti.com/a-better-git-log/
<p>Today I'll share with you a nice 1-line git config that will enhance a lot your git logs.</p>
<p>But let's start from the problem first. When you hit <code>git log</code> you usually get this output:</p>
<p><img src="https://michelenasti.com/images/schermata-2021-09-21-alle-17-53-20.png" alt="" /></p>
<p>There's nothing wrong with it, but it's very verbose, and for the 21 lines of VS code terminal, you only get info on two commits, not much.</p>
<p>Here's the trick.</p>
<p>Open the file <code>~/.gitconfig</code> and add this line at the end:</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># ...other content... </span><br /><br /><span class="token punctuation">[</span>alias<span class="token punctuation">]</span><br /> lg <span class="token operator">=</span> log <span class="token parameter variable">--color</span> <span class="token parameter variable">--graph</span> <span class="token parameter variable">--pretty</span><span class="token operator">=</span>format:<span class="token string">'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'</span> --abbrev-commit <span class="token parameter variable">--branches</span></code></pre>
<p>Tabs are meaningful here.</p>
<p>Now we have succesfully created a new git alias. To use it, we can simply call <code>git lg</code> (note the missing <code>o</code>) and here's what the output looks like:</p>
<p><img src="https://michelenasti.com/images/schermata-2021-09-22-alle-11-43-51.png" alt="(this is a demo project I used for a talk, as you can see the majority of commits were made by github security bot)" /></p>
<p>Here's what you get:</p>
<ul>
<li>much more commits in the same space as before</li>
<li>information is condensed, but all the meaningful info are present</li>
<li>You can clearly see branches, merges, and remotes</li>
</ul>
<p>Now, this post would not exist without the <a href="https://egghead.io/lessons/git-make-my-git-log-look-pretty-and-readable">excellent git course I followed on egghead</a>. Highly recommended if you, like me, had already met git but wanted to know some more advanced features. (<em>Note: i'm not affiliated. To be precise: I am a subscriber! Yes, i do pay for the content.)</em></p>
Evaluating frontend frameworks and not choosing any
2022-01-02T00:00:00Z
https://michelenasti.com/evaluating-frontend-frameworks/
<p>For one of our latest projects we evaluated many frontend frameworks, and this discussion went on for months before taking the decision to, well, not choose a frontend framework. What happened?</p>
<p>Our goal was to build some "widgets" that needed to be:</p>
<ul>
<li><strong>Served thousand times</strong>: we'd like the smallest possible size.</li>
<li><strong>Developer friendly</strong>: the framework should not impose a strict structure to follow. Future widgets may not be developed by us.</li>
<li><strong>Excellent tooling</strong>: development is easy when there's syntax check and hightlight, visual helps to understand what's going on, test utilities, etc.</li>
</ul>
<p>Probably the hardest constraint was the size, that we settled around <strong>15 Kb</strong> gzipped, widget js & css included, images excluded. We don't want to waste the network's capacity with so many bytes. Less bytes sent -> less energy used -> greener planet !</p>
<p>So, let's have a look at what framework was evaluated and why it was discarded.</p>
<blockquote>
<p><em>This is not a post on why every frontend framework sucks, they do not - i love them all. This is a post on the process that took me and the team to reason about the product many times, often making assumptions that later are found not valid, like every time you work on a green field product.</em></p>
</blockquote>
<h2>Angular</h2>
<p><a href="https://angular.io/">Angular</a> was the first one to be discarded. Basically, none of our team has enough experience on it, and angular alone is around 60k, and it imposes a strict structure (and typescript).</p>
<h2>React</h2>
<p><a href="https://reactjs.org/">React</a> is super famous, but if you live on mars and don't know why, basically it's because it allows to create composable UIs with low effort and it has very high rendering performance.
Isn't it surprising that React was discarded? Well, even though it's the most famous frontend framework in the world, <strong>it still requires 120 KBs</strong> in order to work. Much of it because of the ReactDOM dependency, but I couldn't find a way to shrink it.</p>
<h2>VueJS</h2>
<p><a href="https://vuejs.org/">VueJS</a> is smaller, around 30kb, but not <em>that</em> small. Also, it is very opinionated and we don't want to force people of the future to use VueJS.</p>
<hr />
<p>Let's now see other less known frameworks we hit during our evaluation phase.</p>
<h2>Preact</h2>
<p><a href="https://preactjs.com/">Preact</a> is a library that uses the same React syntax (who-ho!) but it's only 3 Kb. It has a solid user base that made us prefer it against all other react-like libraries, like <a href="https://www.infernojs.org/">Inferno</a>.</p>
<p>Well, after a while we saw that if you imported hooks which are basically needed for everything, you had to add 7 Kb more, but we were still in the budget. For a long time we believed we were going to use Preact for our widgets. We even developed a first version with it.</p>
<h2>Svelte</h2>
<p><a href="https://svelte.dev/">Svelte</a> is the new kid on the block in the frontend framework space and I can only say good things about it. Contrary to all previous libraries that are essentially interpreters, Svelte is a <em>compiler</em>, this means that it's a command-line tool that will spit up code that is rendered by the browser as-is, without side loading THE library (react, vue, angular).</p>
<p>Svelte created the smallest bundles, smaller than the preact versions of the same widgets. However, the syntax is different than what the rest of the world is used to and the user base is very small compared to the react world. In the end, we judged svelte too risky to depend on. Maybe in 3-5 years Svelte will be a good choice, but not now!</p>
<h2>Lit and WebComponents</h2>
<p>The last technology we decided to go was <a href="https://www.webcomponents.org/">Web Components</a>. These, in theory, fulfill all our needs: webcomponents are a standard technology supported by all major browsers, you only have to write Js and that's it, no?</p>
<p>Well, no. Writing webcomponents without any external help is difficult and verbose, because the API is very basic and that's why a bunch of libraries have risen to help the developer. One of the most famous is <a href="https://lit.dev/">Lit</a>. At the end of the day, Lit would add some polyfills to standardize what's supported by browsers, plus some helper methods to write components in a saner way.</p>
<p>This would result in 5KB more, but the developer community is basically skipping webcomponents altogether. Probably, the fact that webcomponents do not offer routing, complex state management and all the other bells and whistles, is stopping the community to even test it out. If you go with Lit you have to choose other external projects to fulfill your needs, and this is worse than relying on 120Kbs by react and its entire ecosytem. Last but not least, writing comq qponents in Lit is problematic because the actual HTML lies in a string. it's very easy to mess it up if you're not using an editor extension. And you won't imagine how many people will not install any extension because of their inner beliefs...</p>
<h2>Server Side Rendered frameworks</h2>
<p>We did a test with this too. My framework of choice was <a href="https://nextjs.org/">NextJS</a> , that uses React under the hood. What is a Server-Side Render framework (SSR) from now on? Essentially, it creates a website app that is pre-rendered on the server, using React syntax, and a file-system based routing.</p>
<p>The bonus is you get fast rendering of the component, because the HTML of the component is rendered on the server and sent through the wire, so the browser can instantaneously parse and display it.</p>
<p>The minus is that, in order to execute JS events on the loaded DOM, the browser must receive the JS bundle that contains the library + your JS code. This means that you simply moved React loading later in the chain, but you didn't remove this step altogether. Anyway, SSR methodology is now the de facto standard if you want to develop websites. But I want to build widgets.</p>
<h1>Two cents on the methodology</h1>
<p>In order to evaluate all those libraries, I first had to understand <strong>what was our goal</strong> then I had to decide <strong>if the library would be a good fit</strong> for it.</p>
<p>We probably realized <em>what was our goal</em> after many iterations on the product. At start, we believed we wanted one thing, then we realized we needed more (freedom) or less (KBs). Really, this process was not linear, we runned in circles till we eventually figured out, challenging every assumption all the time. It took a lot, around 6 months.</p>
<p>Regarding the library being a good fit, this was more technical and the framework evaluation was more static. Basically I prepared a simple widget and tried to set it up in all of the previous frameworks. Even setting up the projects was not straightforward, because I am not creating a SPA (<a href="https://create-react-app.dev/">create-react-app</a> is not a good fit, for example) and I wanted to be able to understand every step, from transpiling to minification. We didn't need many features, like bundle splitting, or service workers, that many project starters integrate without asking.</p>
<p>So, for every project, I had to write my own configuration for <a href="https://webpack.js.org/">webpack</a>, or <a href="https://rollupjs.org/guide/en/">rollup</a>. This configuration work was not really accounted at start, but I am glad I did. With it, I could take real misurations for every framework and also I learned a lot.</p>
<h1>So, who won?</h1>
<p>In the end, we figured out that</p>
<ul>
<li>we don't want to add any extra KB that is not strictly necessary.</li>
<li>Our widgets are very simple, interaction is usually very very limited.</li>
<li>We don't care about IE11</li>
</ul>
<p>And our choice become... <a href="https://ejs.co/">EJS</a>.</p>
<p>If I had to give my own a real-world definition of EJS, I'd say it's a library that will allow very complex string substitutions inside javascript strings. You define a template, that usually it's HTML, and a JSON of options, then pass both to EJS, and EJS will render the HTML using the provided options. One option may be, <code>color: red</code> for the background color; another option may be <code>numberOfElements: 5</code> and EJS will iterate five times on a piece of HTML to create 5 subcomponents.</p>
<p>modularity is obtained via <code>include</code> statements, and JS does not rely on any third party library, it uses plain old vanilla javascript to do everything!</p>
<p>We did lost something with this choice. First, we lost compatibility with a couple of tools that we really wanted to use. One of them was <a href="https://storybook.js.org/">storybook</a>, that is a super-nice preview system. We had to develop our own frankenstein to do the same.</p>
<p>Another thing that we lost is editor syntax check and highlighting. Even though there's an EJS extension for visual studio code, it does not understand EJS tags in css or JS and it treats those as errors.</p>
<p>So yes, we definitely lost on the tooling, that was one of our requirements, but we don't think it's that bad in the end. Our choice does not need anyone to learn anything, and one can create a widget in no time!</p>
<h2>Conclusions</h2>
<p>You may have understood that the right tool for the job is not the one that everyone is talking about, but it always depends. This post is not about my day job, that is nothing different than other jobs; it's more about a process, a going-back-and-forth from the product to the techincal specifiation, 'till we eventually found out what worked best for every use case.</p>
Number one quality for remote working
2022-01-04T00:00:00Z
https://michelenasti.com/write-clearly-remote-working/
<p>You want to know the best skill for a remote worker? Without any doubt, it's the ability to <strong>write clearly</strong>.</p>
<p>As a remote worker, i spend a huge part of my day reading and and answering to my coworkers. I need to be as clear as possible when describing my problems and the possible solutions. This usually happens on Slack, but who knows what will be relevant in 10 years.</p>
<p><strong>Writing clearly</strong> is a <em>soft</em> skill, but the good news is that <strong>it's a trainable skill</strong>. As all trainable skills, repetition is the key. Writing is the best way to train it it, and this is one of the reasons I started this blog (thousand) years ago.</p>
<p>I've never been a great writer; in high school my essays were just <em>average</em> and this was certified by my marks, that were around 6 (out of 10).</p>
<p>One day, however, i decided to start a (now defunct) blog on <em>Windows Live Spaces</em> and this was the start of my writer's life. If i read my backup from those days, my first articles were rather embarassing. I think my writing started to improve around the 100th written blog post.</p>
<p>At some point, however, i noticed that there was some pleasure in good writing and then I found another personal kink: explaing things clearly.</p>
<p>I started with politics, trying to understand and explain why things were happening that way, but then I moved to other subjects, for example my field of study. The idea of writing articles about interesting concepts was an incentive to study those things even better.</p>
<p>There's, unfortunately, another side of the medal: time. If you simply study something you'll need <em>X</em> minutes to fix it in your mind; if you decide to write it down, you have to reserve 2-3_X_ time. And usually you don't have that.</p>
<p>This means you need a huge motivation to write a blog about your discoveries; however, once you get used, you end up having the gift of clarity, the possibility to switch to public speaking easily, and the art of explaining difficult concepts with simple words. No doubt you'll enjoy also some benefits from your social bubble.</p>
<p>And that, my friend, is the secret sauce you need to play along in a remote team.</p>
My problem with job junting
2022-02-07T00:00:00Z
https://michelenasti.com/dev-vs-company-expectation-hiring/
<p>Whenever I scroll a job board, I have a feeling that the things that matter to a company are (in <em>their</em> order of importance):</p>
<ol>
<li>The technologies that the candidate knows.</li>
<li>The care for the product.</li>
<li>If the candidate is a good fit for the team.</li>
</ol>
<p>Let's see how (and if) companies try to assess those points.</p>
<blockquote>
<p>I am assuming that money is not an issue here.</p>
</blockquote>
<h2>1. How companies assess candidate's knowlegde</h2>
<p>Probably <strong>the only thing that companies do well these days is to assess that a candidate has the requested set of skills</strong>. Your stack is Kubernetes, NodeJS, R, Backbone.js ? You know what to ask.</p>
<h2>2. How companies assess if the developer is good for the product</h2>
<p>Here, things start to become more imprecise. Usually, companies write a product description in the job listing, and that's it. Then, during the various steps of the job interview, the candidate is informed by the recruiter/manager about the company, the current state of the project, what he is needed for, what will be his responsibilities.</p>
<p><strong>Startups usually do this step in a better way than bigger companies</strong>, because startups have to become profitable in a short time period and all team members are required to have a clear vision/direction of where the product wants to go. A candidate that has opinions, or that helps shaping the future of the product, is needed (required!) but nobody practically assesses this skill.</p>
<p><em>Enterprises</em>, on the other hand, have thousand of teams (and open positions) and they have no idea where the candidate may end up. So they tend to hire generalists that can play well in many teams. <strong>Specs will arrive from the "Specs Department", and if the specs are wrong or bad you'll only know when your customers are not using your feature!</strong></p>
<h2>3. How companies assess that a developer is a good fit for the team</h2>
<p>In this field, only a few companies try to do something upfront and usually they do it unconsciously; for example:</p>
<ul>
<li>try to let the candidate speak to as many people as possible, from future colleagues to managers;</li>
<li>try to understand, during whiteboard/coding assessments, if the candidate is not only competent but also nice to work with;</li>
<li>"hire and wait", i.e. you hire a candidate and if he is crap you fire him. If the candidate does not create problems to the team, we can safely assume he is a good fit. Good job, recruiters!</li>
</ul>
<p><strong>The sad reality is that the third option ("hire and wait") is the most used</strong> by all companies; nobody really cares that the team is happy with the new hire until the new hire is a problem.</p>
<hr />
<p>Apart from the fact that job searching is <strong>time consuming</strong> for both parties, I see another BIG problem here. From the developer perspective, here's the list of priorities:</p>
<ol>
<li>The team is a good fit for me.</li>
<li>the product being built is one that I can help build.</li>
<li>Lastly, I'm really zero-concerned about the technologies being used.</li>
</ol>
<p>... exactly the inverse of the previous list, LOL.</p>
<h2>1. Is the team a good fit for me?</h2>
<p>The best thing about my job is that I've met a lot of people in my 10 years of experience and I know who I like to work with and the price of toxic hire. When you work with the right people you really don't care about work because you <em>want</em> to work.</p>
<p>So, for me, <strong>I prefer to work with positive people</strong>, that are nice and also funny, and that can crack a joke.</p>
<p>And, <strong>I <em>love</em> to work with competent people</strong> that stays updated, that continously study new things, that can highlight or predict new practices / tendencies in IT.</p>
<p>And finally, <strong>I prefer to work with people that treats me as an adult</strong>, by saying the hard things when things are difficult and share successes when things go well.</p>
<p><strong>The problem</strong>: there's no way to know the team before actually working with them.</p>
<h2>2. Am I a good fit for this product?</h2>
<p>After being in the industry for 10 years I know that I am a good fit for startups and companies that build their own product, because I love to discuss about how to enhance it. I don't care if it's a nuclear power system administration system or a tool to buy new followers on instagram. <strong>I like being part of the product making</strong>: it's like an art, where you have to write a new song and you have to fit the words on some new music you've just composed. So, if the product is nice, this is a big plus.</p>
<p><strong>The problem</strong>: Sometimes products are dedicated to a small set of customers, that you cannot see the value beforehands.</p>
<h2>3. Do I know the technologies?</h2>
<p><strong>This is the least thing I care</strong>. The fact is, after 10 years in software development, I never decided to go in a place because of technologies. (I told you a lie: the only time I changed company to one with the same stack, I regretted it - not for the stack itself; there were other issues too).</p>
<p>When I started my career, Java was the king of enterprise with Spring & Hibernate, GWT was the main frontend framework, jQuery was used everywhere, and the first code sharing tool I was exposed to was SVN.</p>
<p>After a few years, I found myself working in Typescript, Docker, NodeJS, Python & PHP... Very different things from what I started. All of this was either learned on the job or with a book, in no more than some days. So... I am confident enough that you can learn technologies in a small amount of time, expecially if you use them daily for work.</p>
<p><strong>The problem</strong>: companies want to hire people that is using the same stack because they want to minimize new hire's time-to-code.</p>
<hr />
<h2>Can we fix this?</h2>
<ol>
<li>there's nothing wrong in assessing the candidate skills; but <strong>please, companies, do not decide who to hire based only on their knowledge of a specific language...</strong> Nobody can know everything but everyone that take this job seriously can learn almost everything in no time. Yes, the price to pay is the mentorship for the first few weeks. But that would happen anyway. And th price of a bad hire is much higher.</li>
<li>As a company, <strong>am I hiring a <em>robocoder</em> - one that can code everything but with zero idea of what the product is supposed to do - or a <em>thinking head</em>?</strong> The first ones may produce great code, without any value to the company. The assumption here is that your company has a very good Specs Department that will exactly define every task with inputs and outputs, without errors, inaccuracies, conflicts. If you have that, coooooool but... i don't beleve you :D So, thinking heads - that study also the domain problem, the users, the</li>
<li><strong>The power of a good, cohese team is that they know how to make things done better than a single individual</strong>. My advice is to let team members - of the team where the new hire will be put - to meet and interview candidates and decide who to integrate into their family.</li>
</ol>
Going to rebuild this blog with 11ty
2022-02-12T00:00:00Z
https://michelenasti.com/rebuild-eleventy/
<p>In this period I am studying a lot of concepts, some which I had forgot and others I didn't have the time to study, so a bunch of new blog posts will come, I thing weekly.</p>
<p>However, I am not satisfied with <a href="https://jekyllrb.com/">Jekyll</a>, my actual blog sytem. The main reason is that it probably arrived at the end of its life, with no developers taking care or pushing it forward.</p>
<p>The second reason is... <strong>Ruby</strong>. Even though it's a very nice programming language, every time I need to work on my blog on a new computer, I have to reinstall the ruby interpreter. <a href="https://michelenasti.com/2015/03/installing-ruby-on-rails-on-mac-10-10-is-a-pain/" title="Installing Ruby On Rails on Mac 10.10 is a pain">This is something I've always found very painful</a>. I am not a ruby programmer so I cannot understand the internals of Jekyll, neither write a plugin for it, and I find this limiting.</p>
<p>Another big pain point is it's slowness. It can take around 30-40 seconds to rebuild my blog, which is archaic compared to newer blogging systems.</p>
<p>This is sad, because in the past I've written a lot of articles about jekyll:</p>
<ul>
<li><a href="https://michelenasti.com/2016/12/22/jekyll-e-github-in-pratica.html">Jekyll & Github in pratica</a> (in Italian)</li>
<li><a href="https://michelenasti.com/2016/12/18/le-tecnologie-che-compongono-un-blog-jekyll.html">Le tecnologie che compongono un blog Jekyll</a> (in italian)</li>
<li><a href="https://michelenasti.com/2016/12/15/le-piattaforme-di-blogging-a-confronto.html">Le migliori piattaforme di blogging (per un developer) a confronto</a> (in italian)</li>
</ul>
<p>So, with some sadness in my heart, it's time to rebuild my blog in another platform.</p>
<p>I've considered some other options:</p>
<ul>
<li><a href="https://gohugo.io/">Hugo</a> - written in Go, very fast, however I am not a hugo developer.</li>
<li><a href="https://nextjs.org/">Next.js</a>, <a href="https://nuxtjs.org/">Nuxt.JS</a>, <a href="https://kit.svelte.dev/">SvelteKit</a>, <a href="https://www.gatsbyjs.com/">Gatsby</a> - Those are super interesting but I think they're an overkill for a blog. Also, they require me to change the structure of my blog, and I don't want to do that now.</li>
<li>Wordpress - no dinosaurs, please</li>
<li><a href="https://substack.com/">Substack</a> - nice place to create newsletters, but to retain the spirit of this blog, I'll create my own mailing list system.</li>
<li><a href="https://dev.to/">dev.to</a>, Medium - nice platforms if you want to start very fast, but I don't want to be limited by a platform I have no control on it.</li>
</ul>
<p>So, what I choose in the end? Meet <a href="https://www.11ty.dev/"><strong>11ty</strong></a> (pronunced: <strong>eleventy).</strong></p>
<p><img src="https://michelenasti.com/images/11ty.png" alt="" /></p>
<ul>
<li>it's written in javascript, and this allows me to understand also what's under the hood.</li>
<li>It supports liquid, markdown, and all the technologies I already use in Jekyll.</li>
<li>Full of plugins, configurations, etc.</li>
<li>It supports some features out of the box, like pagination and tags.</li>
<li>It's supported by <a href="https://www.netlify.com/">Netlify</a>, my current hosting platform.</li>
<li>It's the best tradeoff between how much work I have to do and the features I'll gain.</li>
</ul>
<p>Here's a quick list of things I'll have to check after the migration:</p>
<ul>
<li>All articles and pages must be at the <strong>same URL</strong> as before. This is very important for SEO and discoverability.</li>
<li>all <strong>links must work</strong> exactly the same and point to the same places. Again, SEO and discoverability are my main drivers here.</li>
<li><strong>metadata</strong> for sharing on social networks should remain the same.</li>
<li>the website must <strong>look good</strong> on every display.</li>
</ul>
<p>So, this blog will stay on hold until I have migrated everything to 11ty. This will take some time. Hope to see you back when it's time!</p>
This blog, revamped
2022-03-25T23:00:00Z
https://michelenasti.com/blog-revamped/
<p>So, here it is! my redesigned blog is finally live.</p>
<p>It has just been <a href="https://michelenasti.com/rebuild-eleventy/">migrated to 11ty from jekyll</a>, and redesigned, from a theme called <a href="https://html5up.net/strata">Strata</a> to something super-custom heavily inspired to <a href="https://gridsome.org/starters/gridsome-blog-starter/">Gridsome blog starter</a>.</p>
<p>Migration itself was not an easy task; but internet is full of migration guides. The issue I spent most time on was checking that all my previous permalinks were not changed. I'll probably write another post about that because it ended up in a script that I think it's useful for most.</p>
<p>I think it's the right time to write a bit about the <strong>story</strong> of this "blog", that I consider my side project. Usually people work on apps, startups, or whatever. In my case, I think I have two pet projects, this blog and the developer's community of my area, called DevDay. Let's analyze this blog in this post.</p>
<h2>The origins: MSN Live Spaces</h2>
<p>I started blogging back in 2005, I think. At the time blogging was cool, since there were no social networks yet, and <strong>everybody was on chat apps like MSN</strong> (the favourite of my circle), from Microsoft. This chat app was the de-facto standard for us youngsters. It also offered a blog platform called <em>Live Spaces</em> (later acquired by Wordpress). I loved sharing my photos, or very stupid stories about my university exams and nights out with friends. When I pushed an article, a new badge would appear at the side my name in the chat app.</p>
<p>All of a sudden I started hitting the limits of a proprietary platform. I had no control over themes, I couldn't easily export my own posts. The best browser to upload photos was Internet Explorer!</p>
<p>I started looking for other blogging platforms and, at the time, the most advanced products were CMSs like: <strong>Joomla, Drupal</strong>, and ...<strong>Wordpress</strong>.</p>
<h2>The first upgrade: a (now defunct) personal blog</h2>
<p><strong>Worpress,</strong> back in the day, was on the edge. I think I decided to migrate when Wordpress just hit version 2. So, that was the moment I bought my first - now defunct - domain, around 2008-2009. However, my blog was veeery small and followed only by some close friends. I think the latest post was added around 2013.</p>
<p>Around 2015 I decided to dicth my "personal" blog because I had no interest at all in writing about myself (and being read by nobody). So I decided to buy another domain more linked with myself (this one!), and to start again with Wordpress.</p>
<h2>Michelenasti.com is born: back to Wordpress (~2015)</h2>
<p>My main goal in this phase was to <strong>document my progress in javascript learning</strong>. Javascript in 2015 was just being revamped and nobody in my team was able to work in it. I decided to learn this skill and use that as a competitive advantage. This was a choice that brought me a lot of returns.</p>
<p>Wordpress is fine for a lot of use cases. For example, it is a good blog engine under the hood, however it is mostly used now for media projects like newspapers, landing pages, corporate websites. There are some downsides however:</p>
<ul>
<li>You need a plugin to write code in your posts. It's a pain in the ass to write inline comments when referring to a variable, like <code>this</code>.</li>
<li>You need to host a <strong>database</strong>. This introduces some latency, security, backup issues.</li>
<li>You need <strong>PHP</strong>, and back in the day I was not very proficient with its syntax.</li>
<li>Also, Wordpress is known for requiring a lot of <strong>maintenance</strong>. For example: upgrading to latest version, checking that plugins do not break, checking that memory is enough, handling backups, and check that nobody is attacking your website.</li>
</ul>
<p>You end up doing maintenance only and never writing content. That's not what I wanted to do in first place. So after a while I decided to switch to Jekyll.</p>
<h2>Jekyll (~2016)</h2>
<p>Jekyll is a static site generator. Instead of a database, you can create a file per every article. This is so cheap to achieve and to serve, that many popular services are offering static hosting for free. I decided to switch because almost all blogs I was already following were done in Jekyll, and this gave me the guarantee that I was on the right path; also, there is a nice integration with Github called Github Pages, and you can host a subdomain for free.</p>
<p>I learned a lot with Jekyll. At the time I used to work for an enterprise that was redesigning a bunch of their websites, I proposed (and obtained) to rewrite all of them in Jekyll, it was a huge success. They're still in Jekyll to this day.</p>
<h2>11ty (2022)</h2>
<p>Welcome <a href="https://www.11ty.dev/">11ty</a>, then. Honestly, I felt to do the redesign because my previous website looked old. But I still plan to write what I larn, what I do, and what i discover. I hope you enjoy,</p>
<p>Together with 11ty, my current blogging setup is composed of:</p>
<ul>
<li><a href="https://forestry.io/">Forestry</a>, to write articles in a backoffice like wordpress admin;</li>
<li><a href="https://www.netlify.com/">Netlify</a>, a static site hosting, with many more features;</li>
<li><a href="https://disqus.com/">Disqus</a>, to handle comments.</li>
</ul>
<p>I am not affiliated with any of these services and I use their generous free tiers.</p>
Some tricks for 11ty that I learned along the way
2022-04-02T22:00:00Z
https://michelenasti.com/some-tricks-for-11ty-that-i-learned-along-the-way/
<p>In this post I'll describe some 11ty tricks I've learned while setting some features of this blog.</p>
<h2>Create a collection from a folder</h2>
<p>Coming from Jekyll, I had all my posts in the <code>_posts</code> folder. If you follow the default guide from 11ty, the first impression is that you have to add a <code>posts</code> tag to all your article.</p>
<p>Unfortunately this is not the right approach. Obviously <a href="https://www.11ty.dev/docs/collections/#getfilteredbyglob(-glob-)">this is also described in the official website</a>, but it's buried under a lot of other stuff. You can create a custom collection by adding this in <code>.eleventy.js</code> file:</p>
<pre class="language-javascript"><code class="language-javascript">eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">"posts"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">collectionApi</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> collectionApi<span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">"_posts/**/*.md"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This will create a classic <code>collections.posts</code> that you can later use in your templates.</p>
<h2>Create a page for every tag</h2>
<p>I followed the guide <a href="https://michelenasti.com/some-tricks-for-11ty-that-i-learned-along-the-way/">zero-maintenance tag pages</a>. My only change is, since I have some tags with spaces, to use the <code>slugify</code> filter:</p>
<pre class="language-markdown"><code class="language-markdown">...<br /><br />permalink: /tags/{{ tag | slugify }}/<br /><br /><span class="token hr punctuation">---</span><br />...</code></pre>
<p>Unfortunately, this error started to appear:</p>
<pre class="language-shell"><code class="language-shell">11ty<span class="token punctuation">]</span> <span class="token operator">></span> Output conflict: multiple input files are writing to <br /><span class="token variable"><span class="token variable">`</span>_site/tags/javascript/index.html<span class="token variable">`</span></span><span class="token builtin class-name">.</span> Use distinct <span class="token variable"><span class="token variable">`</span>permalink<span class="token variable">`</span></span> <br />values to resolve this conflict.<br /> <span class="token number">1</span>. ./tags.njk<br /> <span class="token number">2</span>. ./tags.njk<br /><br /><span class="token variable"><span class="token variable">`</span>DuplicatePermalinkOutputError<span class="token variable">`</span></span> was thrown:<br /><span class="token punctuation">[</span>11ty<span class="token punctuation">]</span> <span class="token punctuation">(</span>Repeated output has been truncated…<span class="token punctuation">)</span></code></pre>
<p>What's going on? it's not very clear, it says that the file <code>tags.njk</code> is trying to create multiple files that are linked to the same page, <code>_site/tags/javascript/index.html</code>.</p>
<p>Suppose you come from another blogging platform and you have housands of posts tagged with non-uniform values. For example, I had <code>javascript</code>, <code>Javascript</code> or <code>JavaScript</code> as tags of different articles. Basically, they are three different things for 11ty, and when we're using slugify they all try to write to the same file, which is an error for this blog system.</p>
<p>Unfortunately this is not documented anywhere and I had to debug the source code + ask in the <a href="https://www.11ty.dev/blog/discord/">official Discord server</a>, that I found very useful.</p>
<p>The solution? Find the offending tags and write all of them in the same way. I had to do various find&replace to fix all of them, and now I have to remember to always write tags in lowercase, but I don't think it's a problem once you know.</p>
<blockquote>
<p>Note: whenever you create a link to tags, you must always use <code>slugify</code> filter.</p>
</blockquote>
<h2>Create a page with all tags</h2>
<p>After switching from Jekyll to 11ty, Google Search Console started complaining that one of my pages is missing: it was a page with all my tags listed. I decided to create a page that lists all my tags, so search engines can crawl them easily.</p>
<p>To do that, I first had to add a new filter in 11ty to sort tags by key:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">//.eleventy.js</span><br /> eleventyConfig<span class="token punctuation">.</span><span class="token function">addFilter</span><span class="token punctuation">(</span><span class="token string">'sortObjectByKey'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">collection</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> entries <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span>collection<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> toReturn <span class="token operator">=</span> entries<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">entry1<span class="token punctuation">,</span> entry2</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>entry1<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator"><=</span> entry2<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span><br /> <span class="token keyword">else</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span><br /><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> toReturn<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Then I used this filter in my <code>tags_all.njk</code> page:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="token front-matter-block"><span class="token punctuation">---</span><br /><span class="token front-matter yaml language-yaml">layout: default<br />permalink: /tags/<br />title: Tags</span><br /><span class="token punctuation">---</span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Tags<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span> <br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span><br /> <br /><span class="token code keyword"> {% set orderedCollection = collections | sortObjectByKey %}<br /> {%- for tag, posts in orderedCollection %}<br /> <li><a href="/tags/{{tag | slugify}}/"><strong>{{ tag }}</strong></a> - {{ posts | length }} posts</li><br /> {%- endfor -%}<br /> <br /> </ul><br /> </article></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span></code></pre>
<h2>...In the end (it doesn't really matter)</h2>
<p>These 3 tips are not something that will earn me an award in Computer Science, but it's stuff I lost my time on, so some of you may find this stuff useful. Enjoy :)</p>
How DNS works? A refresh of the DNS algorithm and architecture.
2022-05-11T22:00:00Z
https://michelenasti.com/how-dns-works/
<p>Recently I started asking myself questions about how DNS works. It turns out I forgot almost all I learned at university (damn frontend development!), so I wrote some notes to refresh my memory.</p>
<hr />
<p><strong>DNS</strong> stands for Domain Name Server. It is used by computer to find IP addresses of domains<sup class="footnote-ref"><a href="https://michelenasti.com/how-dns-works/#fn1" id="fnref1">[1]</a></sup>.</p>
<p>When a browser needs an IP it asks a <strong>DNS Resolver</strong>.
The resolver runs this algorithm:</p>
<ul>
<li>if it's not in cache, queries one of the 13 root nameservers. These IPs are hardcoded. The resolver will choose the fastest one.</li>
<li>These root nameservers are nowadays replicated in hundreds of instances so every geographic region has a lot of those servers very close to the them. These nameservers are operated by different companies to ensure indipendence, transparency and adherence to standards.</li>
<li>The root nameserver does not contain individual entries, but it contains the IPs of top domain nameservers . So, if you ask <code>michelenasti.com</code> , it will respond with <code>.com</code> nameservers (more than one, in order to be fault-resistant).</li>
<li>the DNS Resolver now queries the <code>.com</code> nameserver and it will respond with <code>michelenasti.com</code> nameserver.</li>
<li>the DNS Resolver asks <code>michelenasti.com</code> nameserver, that in my case is currently hosted on Netlify, and it will <em>finally</em> return the IP address of <em>something</em> <sup class="footnote-ref"><a href="https://michelenasti.com/how-dns-works/#fn2" id="fnref2">[2]</a></sup> that will return my website.</li>
<li>Finally, the DNS resolver responds back to the browser with the IP address.</li>
</ul>
<blockquote>
<p>Note: for every step, the DNS caches every result. Also, these results have a Time-To-Live (TTL) so if these results do change, they will be refetched after the time expires.</p>
</blockquote>
<h5><strong>Question:</strong> why there are 13 root nameservers?</h5>
<p>Since the #UDP spec says that datagrams less than 512 bytes should not be fragmented, DNS designers thought to restrict the number of root nameservers to 13 (<code>32 * 13 = 416</code> bytes) leaving some space for future.</p>
<p>every address takes 32 bits = 4 bytes; However it seems addresses are hardcoded as 32 bytes. The error here is that in the packet there's not just the IP address but a whole record.</p>
<p>This is however historical and things have changed a lot. <a href="https://unix.stackexchange.com/questions/557799/ip-address-is-of-32-bit-which-means-4-bytes-yet-all-answers-to-question-on-13">A description of how it currently works is here</a>.</p>
<h5><strong>Question</strong>: Which one is chosen at any time?</h5>
<p><strong>BIND</strong>, the Open Source software that <em>was</em> powering up DNS in almost every operating system<sup class="footnote-ref"><a href="https://michelenasti.com/how-dns-works/#fn3" id="fnref3">[3]</a></sup>, usually contacts all root nameservers and then chooses the one with least roundtrip time. <a href="https://superuser.com/questions/527116/how-does-my-browser-locate-the-nearest-dns-root-servers">source</a>.</p>
<h5><strong>Question</strong>: When I connect to a network, what happens exactly? Why when I am at home i use my own DNS server and when I am at work I use the network's dns server?</h5>
<p><a href="https://afteracademy.com/blog/what-is-dhcp-and-how-does-it-work">#DHCP</a> will give to clients, among other informations, also the nameserver for this subnet.</p>
<h5><strong>Question</strong>: What's the AnyCast protocol ?</h5>
<p>Even though there are 13 IP addresses for root servers, in reality there are hundreds of them. Each IP is shared among same servers using AnyCast protocol, so there's a root server close to every subnet.</p>
<p>A description of <a href="https://www.imperva.com/blog/how-anycast-works/">Anycast</a> is here.</p>
<hr />
<p>Hope you have enjoyed my journey back to DNS world. Is there any other question you think I should add?</p>
<p><em>I want to thank <a href="https://twitter.com/bortzmeyer">Stéphane Bortzmeyer</a> for suggesting some corrections to this article.</em></p>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>there are many more use cases for DNS queries. For example, it can find email servers, XMPP servers, authenticating emails, validating servers, etc. <a href="https://michelenasti.com/how-dns-works/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn2" class="footnote-item"><p>in a very simple scenario, it's just one server that answers to requests. Nowadays there are load balancers, clusters, cloud providers that can interfere with your request. So the IP returned is usually going to point to one of those; what happens inside the cloud provider is an internal detail that may vary from company to company, and depending on the desidered level of complexity. <a href="https://michelenasti.com/how-dns-works/#fnref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn3" class="footnote-item"><p>BIND is not the most famous DNS client anymore. Most small "routers" use dnsmasq, operators or local networks also use Unbound, PowerDNS recursor, Knot, etc. <a href="https://michelenasti.com/how-dns-works/#fnref3" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
Lo ha fatto Elon, e ora lo faccio anch'io
2022-06-05T22:00:00Z
https://michelenasti.com/elon-musk-lavoro-remoto/
<p>Si è fatto un gran parlare del fatto che <strong>Musk abbia <em>richiamato</em> i suoi dipendenti a lavorare in ufficio</strong>. Io sono un lavoratore da remoto, e anche se preferirei avere dei colleghi con cui condividere il lavoro e la vita di tutti i giorni, ammetto di essere passato al lato oscuro per un motivo principale: 🤑. Quindi, finchè continueranno a esistere aziende che mi permetteranno di guadagnare più della media senza dovermi trasferire in un altro Stato, ne approfitterò.</p>
<p>Tuttavia <strong>il lavoro da remoto ha i suoi svantaggi</strong> (soprattutto per un datore di lavoro), il primo di questi è l'impossibilità di discutere di un problema davanti a una macchinetta del caffè. Il flusso della comunicazione è bloccato da tastiera e monitor, per cui serve il doppio del lavoro per favorire la comunicazione a tutti i livelli. Bisogna imporsi di: mantenere il focus sul progetto, mantenere le persone allineate sui task, dare le comunicazioni, capire chi sta facendo cosa per riportarlo al management... Nell'azienda in cui questo ha funzionato di più, facevamo <a href="https://michelenasti.com/2019/06/13/i-work-remote-i-do-two-video-calls-per-day.html">due video-meeting al giorno</a> e grazie a quei momenti era come se lavorassimo braccio a braccio.</p>
<p>Due parole a tutti gli imprenditori in ascolto: non "copiate". <strong>Non richiamate le persone in sede solo perchè l'ha fatto Musk.Non diventerete ricchi perchè avrete copiato le idee dei ricchi</strong>. Sarebbe troppo facile! Anzi, la leggenda narra di Musk che licenza in tronco quando sente la risposta "perchè abbiamo sempre fatto così" o "perchè tutto il mondo fa così". Elon ha le sue idee, ed è giusto che faccia quello che ritiene giusto. Esistono centinaia di altri imprenditori che credono in altre cose - alcune stupide come i tavoli da ping pong, altre interessanti come il lavoro full remote - e tutto sta nell'assumere chi mette in pratica quella vision. <strong>Se stai provando ad essere un imprenditore e ti fai influenzare da qualcun altro, secondo me, hai sbagliato mestiere.</strong></p>
Install Samsung ML-2165w printer on newest Macs
2022-07-14T22:00:00Z
https://michelenasti.com/samsung-ml-2165w-printers-mac/
<p>I bought this little piece of hardware centuries ago, a <strong>Samsung ML-2165w</strong>, and it had a lot of advantages for a home user like me:</p>
<ul>
<li>it's a <strong>laser printer</strong>. The toner takes <em>ages</em> to run out.</li>
<li>It costed something around 70€ at the time, probably 2017.</li>
<li>It's small.</li>
<li>It's wireless. I can print from my android phone!</li>
</ul>
<p>So, it has a lot of nice things, but being that old there are also some disadvantages: <strong>official mac drivers do not work anymore</strong> and there's no support from Samsung/HP.</p>
<p>If you navigate to the printer's website (it's an HP website nowadays, because at the time HP bought the whole Samsung printers division) you'll find that the latest available driver is for Mac 10.15. We are - at the moment of this blog post - at Mac 12.4!</p>
<p>Another issue with this printer's driver is that the driver is unsigned. This means that on newer macs you'll get a lot of security warnings and you may think that the printer driver is not installing because of these issues.</p>
<p>No! The reccomended printer driver, simply, does not work.</p>
<hr />
<p>Some years ago, however, Samsung issued a new driver called <strong>Samsung universal printer driver</strong>. It contains drivers for all samsung printers.</p>
<p>So here's the solution:</p>
<ul>
<li>
<p>download the <strong>Mac v11 driver</strong> from <a href="https://driverfresh.com/en/printers/samsung/3-universal-driver.html">this page</a> . Note that on the same page there's also the driver for Mac 10.15. Don't download that one.</p>
<p>If your concern is that the driver is not from Samsung website, I hear you, but that's what actually worked for me. Probably there's a better source out there that comes from a trusted domain. Let me know if you find one.</p>
</li>
<li>
<p>So, the driver you'll be downloading should be version 3.93.01. This driver is signed so there won't be any security warnings.</p>
</li>
<li>
<p>How to install? Open the dmg, click on <strong>MAC_Printer</strong>, then <strong>Printer Driver.pkg</strong> and follow the steps.</p>
</li>
<li>
<p>At some point the driver will ask you to connect the printer. here comes another magic step that nobody tells you.</p>
</li>
</ul>
<p><img src="https://michelenasti.com/images/schermata-2022-07-15-alle-10-25-14.png" alt="" /></p>
<p>Sorry if my screenshots are in italian; however, you must <strong>select the driver from the list</strong> instead of using the proposed one. Why? The proposed one does't work!</p>
<ul>
<li>From the list, select the driver for the Samsung M2060 series:</li>
</ul>
<p><img src="https://michelenasti.com/images/schermata-2022-07-15-alle-10-24-49.png" alt="" /></p>
<p>Voilà! This super old printer now works fine with your new mac.</p>
<hr />
<p><em>I made this article in the spirit of the first web - to help others that may be having my same issue. There's no ned to replace the printer if it still works. Don't fall into the consumism trap!</em></p>
How to do the important things that you keep postponing
2022-08-23T10:00:00Z
https://michelenasti.com/do-not-postpone/
<p>What is the single thing you're not doing, that if done would have a tremendous effect on your personal life? or your work life?</p>
<p>Everyone has a different answer, I have several: blogging, working out, caring of the DevDay community... the kind of things that I keep postponing continously, because there are more <em>important</em> things to do. But after reading a book (keep reading to know the name!), i started questioning myself: why i am not doing these things? What is preventing me from doing stuff that could enhance my life?</p>
<p>Turns out we can divide the "things to do" in 4 categories and put those in a matrix:</p>
<table>
<thead>
<tr>
<th></th>
<th>URGENT</th>
<th>NOT URGENT</th>
</tr>
</thead>
<tbody>
<tr>
<td>IMPORTANT</td>
<td>I</td>
<td>II</td>
</tr>
<tr>
<td>NOT IMPORTANT</td>
<td>III</td>
<td>IV</td>
</tr>
</tbody>
</table>
<p>What is urgent? And what is Important?</p>
<p><strong>Urgent</strong> is something that requires immediate attention; urgent activities are often very visible. For example, a ringing phone is urgent (for the most of us). If we are in the middle of a serious meeting with someone and we hear the phone ringing, we can't resist the urge to see who's calling and probably also aswer and say "I'll call you back".</p>
<p><strong>Importance</strong>, on the other hand, has to do with results. Something is important if it contributes to your goals.</p>
<p>The way our brain works is that we tend to give high priority to urgent things, even when these things are not important, because of their visibility. So, we tend to think that every urgent problem is also important (spoiler: it's not).</p>
<p>Let's go back to the question we asked in the first paragraph. The things that would enhance our lives that we're currently not doing, in which quandrant should fall?</p>
<p>You've probably answered <strong>Quadrant II</strong>, and that's right. The interesting thing is what happens to tasks that end up in this quadrant: they tend to be forgotten and never executed.</p>
<p>But there is a way to do this. If I want to exercise more, I have to reserve some time and do it. If I want to write more blog posts, I probably need to schedule it. Ok... you probably got where I want to go!</p>
<p>Let's rewrite the matrix by putting labels on every quadrant:</p>
<table>
<thead>
<tr>
<th></th>
<th>URGENT</th>
<th>NOT URGENT</th>
</tr>
</thead>
<tbody>
<tr>
<td>IMPORTANT</td>
<td>I - Problems</td>
<td>II - Schedule</td>
</tr>
<tr>
<td>NOT IMPORTANT</td>
<td>III - Delegate</td>
<td>IV - Delete</td>
</tr>
</tbody>
</table>
<p>The quadrant I is now called <strong>Problems</strong> - not because they're real problems, but because the things that are urgent and important must be solved as soon as possible otherwise they'll become problems.</p>
<p>The quadrant II it's called <strong>Schedule</strong> - things that are important but not urgent should be planned upfront and you should reserve time for it. For me, exercising falls into this category.</p>
<p>Tasks that fall in the third quadrant can be <strong>Delegate</strong>d, because they are urgent but they are not important for the success of your life, or business. Here should fall the vast majority of things that seem important but they're not, like the phone call during an important meeting. We can answer later, or hire a secretary to handle them, etc.</p>
<p>Finally, the fourth quadrant is about all the tasks that are just a waste of time, like... scrolling social media. No-one gets any benefit from that, except advertisers :D</p>
<h2>But I am terrible at planning things!</h2>
<p>I feel your pain; I am also a terrible planner and a huge procrastinator. But I also discovered that we (humanity) went through at least 3 generations of time management techniques:</p>
<ul>
<li><strong>TODO Lists</strong>. The first thing we created were to-do lists. However, these lists can grow biggish in small time, so we introduced...</li>
<li><strong>TODO Lists with priorities</strong>. This is a natural evolution of the first generation; there is an improvement because we now do the important things first, but if the list is long enough, we tend to do only the "urgent/important" things and there'll be no time or energy for the rest.</li>
<li><strong>Daily planners</strong>. So, daily planners came along and we started scheduling our days around things to do: meeting with Bob at 11, shop for grocieries at 18, etc.</li>
</ul>
<p>Are these tools effective? Do these techniques help to do "<em>non-urgent but important</em>" tasks? If you tried them all you should already know the answer, that is, unfortunately, <em>no</em>.</p>
<h2>A fourth generation planning technique</h2>
<p>The book <a href="https://www.amazon.it/Habits-Highly-Effective-People-Anniversary/dp/1471195201/ref=sr_1_2?keywords=the+7+habits+of+highly+effective+people+italiano&qid=1661244590&sprefix=the+7+habits%2Caps%2C111&sr=8-2">The 7 habits of highly effectively people</a>, (<a href="https://www.amazon.it/regole-successo-habits-effective-people/dp/8835117798/ref=sr_1_1?keywords=the+7+habits+of+highly+effective+people+italiano&qid=1661245519&sprefix=the+7+habits%2Caps%2C111&sr=8-1">Italian version here</a>) which I am currently reading, contains much of what I've discussed in this blog post. In particular, <strong>Habit 3 - Put First Things first</strong> highlights a 4th generation tool for time management that is an evolution of a daily planner.</p>
<p><img src="https://ik.imagekit.io/xthvogziier/tr:w-300/images/7-habits-effective-people.jpg" alt="The 7 habits of higly effective people" /></p>
<p>Instead of planning daily, <strong>we should start planning weekly</strong>. This way we have enough time to schedule important/non-urgent things that would otherwise be sucked out into other daily activities.</p>
<p>The second thing to do is to identify the <em>roles</em> we play during the week. For example, by living my life I play many roles, like individual, dad/husband, software engineer, community leader.</p>
<p>For each of these roles there are goals I want to achieve, for example:</p>
<ul>
<li>as an individual I want to read more books, and exercise</li>
<li>as a parent/husband, I want to spend some quality, uninterrupted time with my kids and my wife.</li>
<li>As a software engineer, I want to study new technologies, work on pet projects</li>
<li>As a community leader I want to organize new monthly meetups</li>
</ul>
<p>All these activities <em>must</em> be put into the weekly calendar and you should try to stick to the schedule because of the final reward: a better version of yourself.</p>
<h2>Conclusions</h2>
<p>I'm still half through the book, so I can't reccomend it fully, even though it's a nice read that puts a lot of things into the right perspective. I admit that I was worried to be reading a psychological, motivational book because they usually are not my kind. However, there are a lot of ideas to become a better parent, leader, manager (they're two different things) and I'd reccomend the book to those that are looking to improve their lives.</p>
No, i programmatori non sono hacker pazzoidi asociali strambi
2022-08-30T22:00:00Z
https://michelenasti.com/programmatori-hacker-pazzoidi-asociali-strambi/
<p>Come tutti i 35+enni anche io ho un account Facebook, nonostante le nuove generazioni abbiano messo le tende su social di cui io non riesco ad innamorarmi. Su Facebook, insomma, siamo ancora in tanti, anche se vecchietti e rincoglioniti, quindi spesso mi capita di sentire/leggere/ricevere questo tipo di critiche alla mia professione:</p>
<blockquote>
<p>l'informatica riduce le abilità linguistiche, ci lascia con appena 50 parole in testa, ci rende schiavi dei dispositivi</p>
</blockquote>
<p>O ancora lo sfogo di una mamma che, alla vista di una pubblicità che invogliava i bambini a diventare sviluppatori di videogame, scriveva:</p>
<blockquote>
<p>Già i bambini passano tantissimo tempo con i cellulari in mano, addirittura farli diventare sviluppatori? NO!</p>
</blockquote>
<p>Insomma, siamo considerati <strong>hacker, intelligenti ma allo stesso tempo stupidi, disconnessi dalla realtà, con uno scarso uso della logica</strong> che abbiamo addirittura studiato, e siamo <strong>attratti da qualunque cosa abbia chip e silicio</strong>. Ma è davvero così?</p>
<h2>Informatica non è: utilizzo dei cellulari</h2>
<p>Così come saper guidare un'auto non significa saperla progettare o riparare, <strong>chi sa usare un cellulare o un computer non è un informatico</strong>. <strong>Io definisco la programmazione come l'arte di risolvere i problemi tramite dei programmi.</strong> Per alcuni, la sfida è farlo in maniera più elegante possibile (eleganza significa: scrivere codice veloce, facile da manutenere, facile da utilizzare, etc).</p>
<h2>Informatica non è: alienazione</h2>
<p><strong>Qual è l'attività che un programmatore svolge maggiormente?</strong> <strong>Parlare con gli altri</strong>. Il lavoro di informatico è fatto di divisione dei compiti, organizzazione, aiuto, discussione, scelte. Tutte queste cose si fanno parlando o scrivendo, davanti a una lavagna, dietro a un microfono, insomma scegliete voi il come ma il succo non cambia.</p>
<p><strong>Al secondo posto c'è leggere documenti</strong>, purtroppo digitali. Passiamo una quantità di tempo a leggere (e a scrivere!) documentazione tecnica, spesso in un'altra lingua, solo per capire come funzionano e come fare le cose.</p>
<p><strong>E solo al terzo posto c'è scrivere codice.</strong> Per chi guarda la nostra professione dall'esterno, questa appare come l'attività principale, e invece vi assicuro che è quello che si fa dopo che hai svolto i punti 1 e 2.</p>
<h2>Informatica non è: altra informatica nel tempo libero</h2>
<p>Dopo un'intera giornata a computer, chi ha voglia di continuare? Sicuramente ci sono eccezioni alla regola, ma il 99% dei programmatori che conosco, una volta staccata la spina dal lavoro, hanno hobby decisamente analogici: leggere un libro, fare sport, godersi la famiglia, alcuni anche coltivare la terra... Insomma, siamo persone normali a cui piace fare cose normali.</p>
<h2>Informatica non è: giocare ai videogiochi</h2>
<p>Ho una convinzione: realizzare un videogioco è almeno 100 volte più difficile che giocarci. Per realizzare un videogame bisogna immaginare come questo sarà giocato (il gameplay insomma), dargli una storia, realizzare la grafica dei personaggi, scrivere il codice. Poi ci sono altre cose che sembrano imprescindibili al giorno d'oggi, come il multiplayer, il marketing, e il multi-device (lo stesso videogame deve poter essere giocato su telefonini, pc, console...).</p>
<p>Per far si che un videogame diventi un prodotto di successo questo deve essere accattivante e interessante, altrimenti i giocatori non ci giocheranno neanche se pagati. Ecco perchè <strong>i corsi che si rivolgono ai ragazzi usano l'esca pedagogica dei videogame</strong>: <strong>non diventerai ricco, ma imparerai tantissime cose che potrai riutilizzare in decine di altri ambiti della tua carriera, non necessariamente digitali.</strong></p>
<h2>E allora perché l'informatica è demonizzata?</h2>
<p>Innanzitutto l'Italia è un paese profondamente conservatore e noi italiani adoriamo le professioni del passato: <strong>un ciabattino è un mestiere da salvare, i programmatori ci fanno paura.</strong></p>
<p><strong>Culturalmente le famiglie non sono capaci di capire cosa comporti studiare informatica</strong>, e l'associano a quei miti di cui abbiamo parlato in precedenza per cui i programmatori sono alienati strambi. Io stesso ho difficoltà a spiegare alla mia famiglia cosa faccio: gliel'ho spiegato mille volte, usando decine di metafore, ma proprio non ci arrivano, non è nel loro ordine di comprensione. Vuoi fare l'architetto? L'avvocato? L'ingegnere edile? va bene tutto, ma il programmatore no!</p>
<p>Esiste un altro filone secondo cui <strong>l'informatica eliminerebbe posti di lavoro.</strong> La cosiddetta digitalizzazione renderebbe i processi più efficienti e non serviranno più tante figure che, ad oggi, vengono minacciate dall'inevitabile futuro. Pensate al bigliettaio per i mezzi pubblici. Ma cosa è meglio, la possibilità di acquistare biglietti in qualunque momento del giorno e della notte, o dover attendere gli orari di apertura per poterlo fare?</p>
<p>E poi, non voglio cadere nel clichè "solo in Italia", <strong>ma solo in Italia un programmatore inizia con 20k e finisce (se va bene) a 35k (Milano esclusa).</strong> Questa retribuzione è figlia della visione dei <strong>programmatori come operai</strong>, non come persone che risolvono problemi in maniera creativa. Ci rimasi male quando scoprii che un muratore guadagnava quanto me neolaureato, che avevo passato gli ultimi 5 anni a studiare teoremi complicatissimi...</p>
<p>Insomma, <strong>smettiamola di associare i "consumatori" di prodotti digitali con i "creatori": sono due figure diverse.</strong> I "creatori" - non solo i programmatori, ma anche gli scrittori, i musicisti, i marketer... - usano la creatività per realizzare prodotti spesso invisibili, ma che impattano sulla nostra vita continuamente. L'Intelligenza Artificiale invaderà le nostre vite e molti lavori saranno trasformati, ma se c'è una cosa che sicuramente l'IA non riuscirà mai a superare, è proprio la creatività.</p>
How Wine Works
2022-10-10T22:00:00Z
https://michelenasti.com/how-wine-works/
<p>I created this blog post mainly to hold a link to this excellent blog post: <a href="https://werat.dev/blog/how-wine-works-101/">How wine works</a>.</p>
<p>For those of you that don't know what WINE is, well it's a software that allows you to run Windows applications on linux machines. Many games, and Microsoft office, run out of the box with WINE. So you may think it's an emulator? No, it's not - actually it translates Windows system calls to native linux system calls.</p>
<p>I think we all knew how wine worked on a high level, but this blog post makes the extra effort to explain the extra mile. Also, I love when it shows the assembly code for linux and windows, there's so much to learn there, and sometimes I feel my life is too concentrated on high level languages and I am missing out the beauty of dealing with hardware directly.</p>
serverless & 304, Viewability in apps
2023-05-11T00:00:00Z
https://michelenasti.com/serverless-304-viewability/
<p>So, what's going on in my programmer's life?</p>
<h2>Serverless caching</h2>
<p>I've been working on a cloud function from a famous cloud company. This company is, first of all, a CDN company: their job is to replicate a bunch of files we have on our origin servers. (Basically: you may have customers accessing files in Australia and in Europe, but if you let them go all the way to your origin server, which are in the US, they'll see a huuuuge delay in the response. That is not nice. So, some companies have these CDNs that replicate your content geographically, and they're very good at it).</p>
<p>The cool part of this project is that we can create some rules when we get a request. Say, user requests file <code>/abc/def/123.js</code>. This seems pretty straightforward, but we may want to offer our customers the possibility to A/B test two files with the same url, so we can inject a rule and say, for the 1% of the world, serve file <code>123-experiment.js</code>, otherwise serve the plain old <code>123.js</code>.</p>
<p>Then we've decided to use this system to allow another form of caching: <code>304</code>.</p>
<h2>304 Not Modified</h2>
<p>In HTTP, <strong>304 Not Modified</strong> is a header that servers can respond with when it is sure that the client already has a cached version of the file.</p>
<p>But how does the server know that the client has that version? There are essentially two ways:</p>
<ul>
<li>The client sends a GET request with a header <code>If-Modified-Since</code>, that contains a date. So the client is saying, "if the content has not changed since this date, let me know!"</li>
<li>Or, the client can send another header called <code>If-None-Match</code> that contains an identifier of the content or, in HTTP vocabulary, an <code>ETag</code>.</li>
</ul>
<p>We've decided to go with the second approach because of some business factors we had to account. Here's a very nice understanding of how this thing works, in human terms:</p>
<ol>
<li>On all requests, but also at the very first <code>GET</code> request, the server returns the file with an <code>ETag: "123abc"</code> header. <code>"123abc"</code> is an identifier of the content, something that will change if the content will change. You can use the hash of the file, for example.</li>
<li>On the next request from the client, it will do the same request for the same file, but this time it will append the <code>If-None-Match: "123abc"</code> header. Browsers are already programmed to do this if they have received a file with a <code>ETag</code>.</li>
<li>The server will check this header, check the <code>ETag</code> for that file, see that they are the same and respond with <code>304 Not Modified</code> and no body.</li>
<li>if the server detects that the <code>ETag</code> has changed in the meantime, will respond to the GET request exactly as it was a regular GET request (<code>200</code> with a body content). Nobody will notice.</li>
</ol>
<p>Oh! I remember the university class on HTTP, it was huuuge, and tried to explain all the nitty-gritty details of how HTTP 1.1 works, all things that I refreshed while working at this task. For more information, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304">MDN has a lot of info about the topic</a>, if you click long enough :-)</p>
<h2>Viewability</h2>
<p>In adversiting there's this huge topic of viewability: companies will only pay for ads that have been viewed, not for those that have been downloaded. This means that checking that an ad has been viewed by a human is very very important.</p>
<p>There have been thousands of ways to detect viewability, in browsers expecially, but recently the topic became hot with the advent of mobile apps. So, the IAB (The biggest association of advertisers) published this new library called OMID, that has two components: an app SDK (for iOS and Android) and a Javascript part, that goes into the creative.</p>
<p>The javascript part will communicate with the native SDKs and will get some "events" when the ad is in view; with those events we can trigger appropriate actions to record this viewability event.</p>
<p>The most difficult part of this project was to deal with the Google Closure Compiler (that was used by IAB to develop the javascript part) and to test the result on Android and iOS, given that I am not a mobile developer. Oh, that was hard!</p>
<h2>In conclusion</h2>
<p>All these 3 projects were new things for me, I had zero experience with. In the end I am glad I started them, I learned a lot of interesting things along the way. But ultimately, I now feel the urge of learning some basic Android and write a very simple app to test things by miself.</p>
LogSeq: what's good, what's not, what I still haven't figured out
2023-06-12T00:00:00Z
https://michelenasti.com/exploring-logseq-for-notetaking/
<p>I've gone through many notetaking apps in my life, expecially the open source ones. For my job, a very simple notepad is not enough. Let's compare some of them I've used along the years.</p>
<ul>
<li><strong>Evernote</strong>. Nice app, probably the first real <em>premium</em> one. The drawbacks for me are that it's commercial, it's not open source, it's a locked ecosystem. Tested for a while then I started checking out for alternatives.</li>
<li><strong>Google Keep</strong>. Very simple note taking app. For some time it has been my default app. However, it's very limited: I want to do much more with my knowledge.</li>
<li><strong>DS Note</strong> - AKA <strong>Synology Note Station</strong>. Synology is a NAS producer and I proudly own a <a href="https://michelenasti.com/2019/10/27/tips-tricks-from-my-linux-experience.html">Synology NAS</a> at home. I use many Synology services, like <a href="https://michelenasti.com/from-google-photos-to-synology-photos/">Synology Photos</a>, because I want to physically own my data. Sometimes I may write a password, or some thoughs that I don't want to go public. However, DS Note is probably the least loved app by Synology developers. There are a lot of bugs that you hit in a way or another. Plus, the data is not in an exportable format. And you cannot create links. Usability is a mess on mobile devices.</li>
<li><strong>Obsidian</strong>. This was my first attempt to use a serious app for note taking. With Obsidian, note taking becomes a <em>knowledge tracing</em> experience. I've used Obsidian with profit over the last year, and there were a couple of features I really liked, like the fact that everything is Markdown (we should give the author of Markdown a Turing Award, lol), files are phisically living on your device, and you can sync with whatever - I sync with my NAS, but others use dropbox etc. Everything looks cool but I have found some shortcomings, like the fact that notes were mostly disconnected, and I kept organizing notes based on directories and subdirectories, which is not optimal.</li>
<li><strong>Logseq</strong> - the last tool I'm trying to use. It will be discussed here more in detail.</li>
</ul>
<h2>My requirements</h2>
<ul>
<li><strong>Files must be mine</strong>. I don't want my notes to live on hardware that is not mine.</li>
<li><strong>Markdown</strong>. I want to use Markdown to write my notes. Markdown is a very simple way of writing notes with all the fancy stuff we are used to - bold, italics, tables, images, links, code... - and then it's nicely rendered as html by programs. <a href="https://github.com/musikele/blog-eleventy/tree/master/_posts">This blog is written in Markdown</a>. Once you start using markdown it's impossible to go back.</li>
<li><strong>Organize my knowledge</strong>. This is where Obsidian falls short. Obisidian uses a bottom-up approach, so you write the note, and then you connect the note with other pages, which may never happen. My notes were mostly disconnected with very few links.</li>
<li><strong>Write Todo lists</strong>, and possibily handle them as a kanban board. I keep juggling many tasks at once (I know it's bad but life is like that), so I need a place to capture what I am working on.</li>
<li><strong>Have a journal of my day</strong>. I was not into journaling, but the author of a newletter I am subscribed shared the idea of a "house journal", where he kept writing down things about his house, and his life: when he did renovations, who came to visit, etc. Porting the idea to the computer world, I started to feel the need of a place where I could write down what I was working on, what I was thinking, personal notes, etc.</li>
<li><strong>Link all these thoughts with ease</strong>. I want the process of linking stuff together to be totally transparent; I don't want to look for a filename or to phisically create the note; the program should take care of it.</li>
<li><strong>search</strong>. Given that it's plain text, this is probably the easiest part that every program is able to do.</li>
</ul>
<h2>Logseq</h2>
<p>The latest addition to the gang is <a href="https://logseq.com/">Logseq</a>. Installing it is a breeze. However, using it like a pro requires some training: that's why it's not a classic, text only, notetaking app.</p>
<h3>The most important features for me</h3>
<ul>
<li>When you open the app, the first thing you see is today's note. Basically, it's a note with today's date.</li>
</ul>
<p><img src="https://michelenasti.com/images/logseq-diary.png" alt="Sorry for italian. "Diario" means Diary" /></p>
<ul>
<li>The second thing you'll notice is that everything is a bullet point, which can be indented at your will. This is very handy because it puts in effect the <strong>top-down approach</strong>: it forces you to think in topics.</li>
<li>the third thing I appreciated is <strong>how easy it is to create pages and links between them</strong>. You only have to write <code>[[Page name]]</code> and you get a new page created. This is instantly clickable and you can see in the page all the notes (and bullet points) that link there. Also, you can add more text in the page that is not linked to a daily note, which is great for organizing content! Hashtags work too (e.g. <code>#PageName</code>) but you're constrained to not have spaces in your text.</li>
</ul>
<p><img src="https://michelenasti.com/images/logseq-page.png" alt="In this example I clicked on Yamaha mt-07 page." /></p>
<h3>Other things worth noting</h3>
<p>These three features alone made me use LogSeq from day one without regrets. But there is more to the table.</p>
<ul>
<li>Being file-based, I could use the same obsidian directory and I automatically got all old notes imported. All previous sync processes I already had in place are just working fine, again. And since everything is cleartext, you don't loose nothing.</li>
<li>The app has two other features that I'm not using yet, which are <strong>Whiteboards</strong> (the ability to draw on a canvas) and <strong>Cards</strong>, which are a way to structure the content you want to remember.</li>
</ul>
<h3>Where it falls short</h3>
<ul>
<li><strong>TODOs.</strong>. This is the part I feel I am missing something. Currently, you can create a TODO anywhere in the app (daily page, dedicated page...) by simply writing <code>/TODO</code>. However, keeping track of these TODOs is complicated because they're scattered all over the pages. (<em>I am still a first-time user so I may be missing something very obvious.</em>) I'd love to be able to create multiple TODO pages, for example, one for work and another for personal, or hobby tasks; for example, I may have to add in the Work to do "submit expense report" and in the personal todo list "create appointment with doctor". these TODOs may be seen in a dedicated TODO page which can be filtered out by the "category" of the todo (work, personal, hobby...) or all together. This part is complex but probably Logseq developers are hardcore enough to hear my preys.</li>
<li><strong>Documentation</strong>. Logseq website is beautiful. But I felt bewildered when the "introduction" part contained a 70 minutes video. I tried to find a simpler, quicker, gentler introduciton with no luck. There are hundreds of guides to tweak everything, there are also use cases by other people that have bended LogSeq to their own workflows, but nothing for the pure starter. Which makes it very hard to understand if I am doing things the proper way or not. I feel that a 5 mins introduction video that goes through the very basics of the application is the missing part.</li>
</ul>
<h2>Conclusions</h2>
<p>It's been 3 weeks that I am using LogSeq only and I feel my notetaking has taken a new level. My notes are finally connected, everything is easily reachable and searchable. I am now randomly adding things that normally I wouldn't add just because I want them to appear when connected to other notes. I am also mixing personal with work stuff because I feel that the diary page has an immense power on fixing memories around. There's for sure room for improvement, and probably I am using LogSeq at 20% of its capabilities, but this makes it a serious tool for knowledge workers. If you want to seriously keep track of your life, work, thoughts, memories, LogSeq will take this to a newer level.</p>
Bye advertising industry!
2023-09-01T00:00:00Z
https://michelenasti.com/bye-advertising-industry/
<p>My last four years have been incredible, on a professional side. I think it's the right time to recap what happened and how it went.</p>
<p>10 years ago i used to be the classic Italian software developer. I was struggling to pay the bills, not because i wasn't paid well (I was paid <em>average</em>), but because I lived the lifestyle of a high class family son, with the pay of a middle class employee, that in Italy is terrible.</p>
<p>On my way to emerge from anonimity, I tried several things: I read technical books on commute, trying to catch up with latest technologies or to be deeply aware of what I was using at work; I also started attending conferences, started a programmer's meetup in my area, and this blog. All of this was possible because back in the days I wasn't married and had no kids :)</p>
<p>5 years ago, I was one of the very few remote workers in italy; we were so few that I was also called by the local university to describe how remote working is to students. We didn't know that covid would have forced everybody to become remotes :)</p>
<p>One old suggestion to enhance your career was to "jump" between jobs every two years, so that's what I was doing: trying to gain a better position at every new company I joined.</p>
<p>But there was one moment that changed my life forever: when I ended up on hacker news front page.</p>
<h2>Hacker news</h2>
<p>One way I intended to use this blog was to write down the little things I was learning along the way. Some of them would be very trivial, but are the types of things that I google all the time, so why not just "save" them in this personal space?</p>
<p>One afternoon I wrote an article on a javascript obscure feature, <a href="https://michelenasti.com/2018/09/19/javascript-chiamare-funzioni-senza-usare-parentesi-(what!).html">javascript: calling functions without using parenthesis</a>. This was also one of my very first articles in english. At the time, I also self-posted articles on Hacker News (and many other social platforms) hoping to get some traffic. That same afternoon I went to a job interview that went well, they were looking for an experienced frontend dev, and this could increase my pay by (drum roll...) 100€/month ! I was thinking of accepting. Also, the company was in my local town and I got sick of working remotely.</p>
<p>On my way back, my article on Hacker News landed in front page and people were discussing it actively! My phone started buzzing, twitter was on fire, and I started receiving a huge number of emails from people around the world. Questions like, "are you available to move to Germany?" or "would you like to join our startup in Taiwan?" were common that days. Among these emails there was one that stood out: it was written in (wrong) Italian and came from Switzerland. They were asking if I wanted to do a job interview with them in Zurich. I shared the news with my wife and she said, "well, at least you can get a free trip and a lunch!". The next day I was on my flight. It was 2019.</p>
<h2>RTK.io (that no longer exists)</h2>
<p>I finally got an offer which was double than what I was used to in Italy. Now it's the moment to tell you about my impostor syndrome: the very first months I felt like I joined a spaceship to Mars, and my only background was... <em>Javascript</em>. People were used to speak english to discuss very complex problems, and I thought to be able to sustain a conversation in english, well it turns out i wasn't. Or, to be more precise, I had to to do some practice because my words couldn't come out at the same speed of my thoughts.</p>
<p>About this company, that was called RTK.io, I want to highlight a few points.</p>
<ul>
<li>The company operated in the programmatic advertising space; we wrapped a special version of Prebid.js for the needs of our customers and we tried to keep the configuration as simple as possible.</li>
<li>The engineering team was made of up-to-10 people (even though for most of the time we were ~8) and we operated all the infrastructure and the business of the company: servers, networking, reporting, analytics, big data, and of course, writing software in many different languages. I think it was the most badass team I've ever worked with.</li>
<li>Even if it was a remote company, I really felt part of a group. We were of course not the classic corporate grey-soul team, and this was because we were pretty homogeneous: we liked to joke a lot, usually about our home countries, and we also had mostly the same age, so working with them was just fun. I thought this was going to be my last company, because money was more than enough (I was finally saving something!) and in the mornings I was happy to turn on the computer.</li>
</ul>
<p>Everything was going well, and went that well for the whole 2019, when our boss one day calls us to tell that he sold the company to Magnite.</p>
<h2>Magnite</h2>
<p>Magnite is one of the biggest companies in the programmatic space. At the time, numbers said that they were trafficking maaany more ads than us. Our customers were <em>not-so-famous</em> (even though they were doing pretty figures), while Magnite had top tier publishers from the entire world. What was our role in this new company? My biggest fear was that they could dispose us, and bring all this happiness to zero in one night.</p>
<p>Instead, many things were going to click in a positive way. For example, my manager negotiated a salary increase for us. I was, for the first time, exposed to the dynamics of a public company listed in the US stock exchange, where every business decision can lead to a stock price bump or fall. I was also exposed to RSUs (basically, stocks that the company gives to you every X months), intercontinental business trips, and a scale of doing things that I'll probably never see anymore.</p>
<p>But, what happened with the RTK folks? We all joined this new company, but after a year of working together, almost all of them decided to pursue new experiences. I think that the real end of the RTK team was at the start of 2022, when I remained alone.</p>
<hr />
<p>I was thinking to move to another company too, but since I moved to a new team with a new manager, for some time I thought that I've just landed a new job. Everything was new, new colleagues, new products, new procedures, new meetings, new everything. The only thing that didn't change was the advertising space we were already in, and how we were trying to make money out of it. In the end, it didn't go bad: it was one of the best experiences you can do in an enterprise company, and I liked it. So, years 2022-2023 were spent in this new environment, trying to bring value, working at many projects at many different levels, and it was fun!</p>
<h2>Time for a change</h2>
<p>How do you know it's time for a change in your career? I like coding, and I also like when I am "in the zone" and I feel invincible, powerful, super-productive. But as I grow up, the need for taking a different, bigger space in the world comes out.</p>
<p>Again, I was not actively looking for a new job but at some point, through word of mouth, another foreign company asked if I wanted to take the ownership of building their digital solutions branch in Italy. This means that I am gradually moving from coding-only to a more managerial role, and to some extent, it feels natural to me. It's like, all my life I've discussed about effective management, been involved in projects with other people, tried to lead teams, meet deadlines, take care of entire features from prerequisites to production. I've seen many management styles in my career, and we can all recognize a bad one from a good one. The thrill is to be a good one.</p>
<p>So, here we are. New company, kinda new role, completely different industry. An article on my blog, 4 years ago, changed everything. What will I see in my future?</p>
Come To Code 2023
2023-09-26T00:00:00Z
https://michelenasti.com/come-to-code-2023/
<p>Il sogno erotico di qualunque persona del Sud è di mettere sulla mappa il proprio paesino di provenienza, ad esempio realizzando qualcosa di grosso. Nei giorni scorsi ho partecipato alla conferenza <a href="https://www.cometocode.it/">Come to Code</a>, a Pignola (Potenza), e devo fare i complimenti agli organizzatori, ci sono riusciti. Infatti, Pignola è un paesino di 6700 abitanti, a 900 metri d'altezza, eppure c'erano aziende sponsor, speaker, e 200 sviluppatori hanno pagato un biglietto per essere lì. Come?</p>
<p>La mia storia con Pignola inizia tre anni fa quando vengo approcciato per la prima volta dai membri del Plug, il Pignola Linux Users Group, in cui mi si chiedeva se volessi aiutarli a diffondere l'evento. Poi un caro amico partecipa come speaker alla seconda edizione e mi dice: devi assolutamente andarci, è organizzato straordinariamente bene. Dunque quest'anno, complice il DevDay che è un attimo fermo ai box, ho deciso di candidarmi col talk "<a href="https://michelenasti.com/2019/10/21/how-internet-ads-work.html">How Internet Ads Work</a>" (che per inciso probabilmente non potrà più essere fatto in pubblico, visto che <a href="https://michelenasti.com/bye-advertising-industry/">non faccio più parte di quel mondo</a>) e... così è iniziata la magia.</p>
<p><img src="https://michelenasti.com/images/2023-09-26%2008.31.07.jpg" alt="Io che parlo ad una sala di giovani nerd." /></p>
<p>La conferenza, come avrete intuito, si chiama <em>Come To Code</em> ed è stata organizzata in due tracks parallele più workshop pratici. Organizzata nello storico Palazzo Gaeta, è la perfetta sintesi di <em>futuro</em> (come altro potremmo definire i programmatori, la programmazione e l'informatica?) e <em>passato</em> (un palazzo storico, un borgo a 900 metri d'altezza, stradine che si inerpicano con pendenze del 100%...).<br />
Gli speaker della conferenza sono guru del settore: non voglio fare nomi per non rischiare di dimenticare qualcuno, ma mi sono stupito di incontrare persone che normalmente vengono invitate anche all'estero. Gli perdoneremo l'errore di aver accettato la mia candidatura 😀</p>
<p><img src="https://michelenasti.com/images/dettaglio-talk.png" alt="Il summary del mio talk" /></p>
<p>E infine l'aspetto umano. Come speaker siamo stati trattati benissimo. La logistica, seppure complicata per essere "distribuita" in un paesino microscopico, è stata gestita benissimo: nessun disservizio. Le cene sono state tutte fantastiche, una conferenza nella conferenza, per il livello di discussioni che si sono create. Io dico sempre che alle conferenze si va anche per fare un po' di networking e qui, ovviamente, non è mancato.</p>
<p>La Basilicata dunque è sulla mappa, io e altre 200 persone possiamo dire di essere state a Pignola con piacere e profitto e se c'è un messaggio che devo lanciare a te, lettore, è di partecipare assolutamente l'anno prossimo. Perché io ci tornerò!</p>
No-code development with Appsmith
2024-03-03T23:00:00Z
https://michelenasti.com/appsmith/
<p>Fullstack developers are always looking for tools that can help build applications faster. <a href="https://www.appsmith.com/" title="Appsmith">Appsmith</a> is a tool that allows to do exactly that, and after three months of trying, I think there's nothing that can be faster than that. With a huge set of limitations, of course, but nonetheless it's still valuable in many situations. Let's dive in.</p>
<blockquote>
<p>Note: I didn't try Retool, ToolJet and all the other competitors out there. I am sure they have their own set of strenghts and weaknesses, too, but my experience has been with Appsmith only and I can only talk with confidence about this one.</p>
</blockquote>
<h2>What is Appsmith?</h2>
<p>Appsmith is a <strong>no-code</strong>, <strong>open-source platform</strong> that allows to build applications using a drag-and-drop interface. The focus is around APIs and data (you have to provide the data somehow), but I think the level of customization is enough for most use cases.</p>
<h3>Wait, what is no-code?</h3>
<p>No-code is a movement that aims to allow people to build applications without writing code. It's not a new concept, but it's been gaining a lot of traction lately. The idea is to allow people to build applications without having to learn how to code. This is done by providing a visual interface that allows to build applications by dragging and dropping components, and connecting them together.</p>
<p>If you are a developer like me, I know what you're thinking: <em>there's NO WAAYYY I'm going to use such a tool, I am too good at <insert your favourite framework here> and nothing can beat me at this</em>. You are right: you are good and these tools are crap. But there are a few advantages that these tools bring to the table, which I'll try to enumerate here:</p>
<ul>
<li>They allow non-technical people to create UIs that work. By inference, they allow not-so-good developers to make stuff that works, with a decent quality.</li>
<li>They are suitable for a limit set of appliations, but for those types, they offer an extremely fast development experience.</li>
<li>You don't have to think about a lot of stuff - styling, bundling, deploying... other people has already taken those choices for you.</li>
</ul>
<blockquote>
<p><strong>No-code or Low-code?</strong>
Since writing an app with no code is impossible, some in the industry have been starting naming these concepts as "low code", because you don't have to write everything, just a few bits of it.</p>
</blockquote>
<h3>But what exactly is Appsmith?</h3>
<p>It's a webapp. You access via <a href="https://www.appsmith.com/">appsmith.com</a> and sign up. Once you're in, you're greeted with a dashboard with all your apps:</p>
<p><img src="https://michelenasti.com/images/appsmith_dashboard.png" alt="Appsmith's dashboard" /></p>
<p>Creating an app is a matter of clicking "+ New", and then you're redirected to a page that looks like this:</p>
<p><img src="https://michelenasti.com/images/appsmith_new_app.png" alt="Appsmith new app page" /></p>
<h2>The Appsmith way of writing apps</h2>
<p>One of the first steps you'll have to do is configuring a data source. Appsmith can connect with a wide variety of sources, including a bunch of popular SQL databases, or REST APIs, GraphQL, and so on.</p>
<p><img src="https://michelenasti.com/images/appsmith-datasources.png" alt="list of Appsmith datasources" /></p>
<p>Then you'll probably start putting widgets on page. Appsmith comes bundled with a long set of widgets that satisfy many common use cases. Date pickers, multi select boxes, and even charts, most of these are already packed in. The list is long, what you see in the next screenshot is just a start.</p>
<p><img src="https://michelenasti.com/images/appsmith-widgets.png" alt="list of widgets" /></p>
<p>And finally the most complex part: adding interaction and state management to the UI. In Appsmith world, this is done with reactively.</p>
<p>So, imagine you have to get a list of users from DB. You write a query and save it in Appsmith (let's call the query <code>getUsers</code>):</p>
<pre class="language-sql"><code class="language-sql"><span class="token keyword">select</span> <span class="token operator">*</span> <span class="token keyword">from</span> <span class="token string">"users"</span><span class="token punctuation">;</span></code></pre>
<p><img src="https://michelenasti.com/images/appsmith_query.png" alt="query in appsmith" /></p>
<p>Then, you add a table component and "bind" the content of the table to the query. Voilà! The table gets populated with data, and you only have to customize columns.</p>
<p>Imagine you want to do filtering server side. This means that we will add a new input box called <code>inputFilter</code>.</p>
<p>Then we change the query to get this value:</p>
<pre class="language-sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <br /><span class="token keyword">FROM</span> <span class="token string">"users"</span> <br /><span class="token keyword">where</span> <span class="token punctuation">(</span><br /> {{inputFilter<span class="token punctuation">.</span><span class="token keyword">text</span><span class="token punctuation">.</span>length}} <span class="token operator">=</span> <span class="token number">0</span> <br /> <span class="token operator">or</span> name <span class="token operator">like</span> {{<span class="token string">"%"</span> <span class="token operator">+</span> inputFilter<span class="token punctuation">.</span><span class="token keyword">text</span> <span class="token operator">+</span> <span class="token string">"%"</span>}}<br /><span class="token punctuation">)</span><span class="token punctuation">;</span><br /></code></pre>
<p><img src="https://michelenasti.com/images/appsmith_query_2.png" alt="complex query in appsmith" /></p>
<p>Note that this query contains some javascript inside curly braces. The <code>where</code> clause is the important part. The first condition checks if the <code>inputFilter</code> input field is empty; in such case it will return true and the query will return all values. If <code>inputFilter.text.length > 0</code> then the second part of the query is executed, and as you can see we use javascript inside curly braces, to compose our <code>like</code> clause.</p>
<p>And there's much more. Even Javascript classes can be data sources. This means you have the ability to process the data, before sending the query, or after.</p>
<h2>This is cool, but are there any downsides?</h2>
<p>Definitely there are downsides. I'll try to tell the ones we hit after three months.</p>
<ul>
<li>There's no way to customize the look & feel. This is ok for most CRUD, Internal apps, but if your customer is more demanding, personalizing Appsmith can only go that far.</li>
<li>Apps must have the structure Appsmith has defined. For example, you cannot move the pages menu.</li>
<li>Performance: don't expect appsmith apps to be top-notch. Since state management is reactive, at every change the UI has to recalculate what to trigger and what to change. This comes at a cost.</li>
<li>Git integration is not really useful. When you add git to your project, you discover that the whole app is <em>just</em> a big json of keywords that only makes sense to them. All your queries and functions become stringified, meaning you cannot even check if there's something wrong. The biggest problem is that two people cannot collaborate on the same app: merging is impossible because you have to figure out conflicts on json keys. In order to solve this problem, we try to merge PRs as soon as possible, sometimes disrupting the flow of other developers that have to review PRs in a hurry to avoid merge conflicts.</li>
<li>Reviewing PRs is also very limited. For example, you cannot inspect the code for bad things. The only thing you can do is execute the app and check that everything still works. But: nailing down if the query is ok, or if there are no wastes in memory or inefficient code, it's complicated because everything is stringified.</li>
<li>Debugging is another pain point. Apps that become big enough tend to have bugs in the data, for example in queries. Appsmith has a debugging panel that allows you to inspect things, like the return value of a query, or to write console logs, but you cannot stop and debug javascript like you're used to.</li>
<li>It's impossible to reuse components and data sources across apps.</li>
<li>In the eventuality you need a component not provided by Appsmith, you can write your own in an iFrame. This brings in a whole new set of problems, like sending data to and from the iframe, which would require another blog post (that has been my biggest pain point recently).</li>
<li>As a non-coder colleague said, after starting to work on an Appsmith app: "this tool is supposed to be drag n' drop but all I am doing is fixing and writing code everywhere!". That's what happens when your application becomes a Frankenstein made of custom components, iframes, and weird logic.</li>
</ul>
<h2>Conclusions</h2>
<p>So, how's gone with Appsmith so far? The company I worked for has decided to keep it as a prototyping tool, because it really makes development fast. However, we've also reached the point where the features we wanted to implement deserved a fully fledged SPA written in real code. So, in the next months they'll develop the same app in React. In the meantime, if there'll be new features to validate with the customer, these will be implemented in Appsmith first.</p>
<p>Anyway, apart from my specific work case, I think there's room for appsmith development out there. I get asked a lot to make "apps" to store informations, like users, customers, etc - Appsmith is just nice for that. Small apps with very limited features: if that's the case, no need to set up a complex react stack, appsmith will just do the job.</p>