<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Fabien Lasserre – Full stack web developer</title>
    <subtitle></subtitle>
    <link href="https://www.fabienlasserre.dev/en/blog/feed.xml" rel="self" />
    <link href="https://www.fabienlasserre.dev/" />
    
        <updated>2025-04-29T00:00:00Z</updated>
    
    <id>https://www.fabienlasserre.dev</id>
    <author>
        <name></name>
        <email></email>
    </author>
    
        
        <entry>
            <title>Containerized Symfony &amp; Vue.js environment with DDEV</title>
            <link href="https://www.fabienlasserre.dev/en/blog/containerized-symfony-and-vue-js-environment-with-ddev/" />
            <updated>2025-04-29T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/containerized-symfony-and-vue-js-environment-with-ddev/</id>
            <content type="html">
                <![CDATA[
                <p>For the past year or so, <a href="https://ddev.com/">DDEV</a> has been my go-to tool for setting up a development environment. With a simple <code>.yaml</code> file, this nifty tool is capable of providing a containerized workbench with PHP, a MariaDB/PostreSQL database, and Mailpit. It's perfect for anything from WordPress to the most complex Symfony app. However, out of the box it's not tailored to be used alongside Node. Granted, it does come bundled with Node pre-installed, but if you want to use your PHP backend with a TypeScript frontend, it requires a tiny bit of configuration. This is what this article is about. It is inspired by Andy Blum's blog post over at <a href="https://www.lullabot.com/articles/nodejs-development-ddev">Lullabot</a>. My approach is similar but a bit simpler.</p>
<hr>
<p>My web stack of choice is Symfony as an API (either RESTful or using GraphQL) and a frontend using Vue.js. I used to rely on <a href="https://github.com/nvm-sh/nvm">NVM</a> or <a href="https://volta.sh/">Volta</a> and make use of my locally installed Node instance, but I wanted to have something completely portable - something where I could just clone a repo, run <code>ddev start</code> and get to work. To achieve that, we're going to set things up as follows:</p>
<ul>
<li>Set up the PHP container and Symfony</li>
<li>Freeze the Node version and install Vue.js</li>
<li>Add a reverse proxy to access the frontend</li>
<li>Enhance the DX thanks to a Makefile</li>
</ul>
<p>For this project, we're going to use a made-up domain - let's say <code>tinydev.ddev.site</code> - and have the API available at <code>api.tinydev.ddev.site</code> and the web app at <code>app.tinydev.ddev.site</code>. I'm going to assume you already have Docker and <a href="https://ddev.com/get-started/">DDEV installed</a> and ready to use.</p>
<p>To keep things tidy, we'll use the following folder structure:</p>
<pre class="language-conf"><code class="language-conf">/.ddev
 - config.yaml
/backend
 - Symfony files...
/webapp
 - Vue.js files...</code></pre>
<p>The <code>/.ddev</code> folder will contain its configuration file (<code>config.yaml</code>), with the Symfony API inside <code>/backend</code> and our frontend inside <code>/webapp</code>.</p>
<h2>Setting up the Symfony app</h2>
<p>Let's begin by using <code>ddev config</code> to initialize the project, selecting <code>symfony</code> as the application type. The docroot for our project will be <code>backend/public</code>.</p>
<p>Once configured, open <code>/.ddev/config.yaml</code> and add the additional host names (<code>api.tinydev</code> and <code>app.tinydev</code>). The configuration file should look like this:</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">name</span><span class="token punctuation">:</span> tinydev
<span class="token key atrule">type</span><span class="token punctuation">:</span> symfony
<span class="token key atrule">docroot</span><span class="token punctuation">:</span> backend/public
<span class="token key atrule">php_version</span><span class="token punctuation">:</span> <span class="token string">'8.3'</span>
<span class="token key atrule">webserver_type</span><span class="token punctuation">:</span> nginx<span class="token punctuation">-</span>fpm
<span class="token key atrule">xdebug_enabled</span><span class="token punctuation">:</span> <span class="token boolean important">false</span>
<span class="token key atrule">additional_hostnames</span><span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token string">'api.tinydev'</span><span class="token punctuation">,</span> <span class="token string">'app.tinydev'</span> <span class="token punctuation">]</span>
<span class="token key atrule">additional_fqdns</span><span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span>
<span class="token key atrule">database</span><span class="token punctuation">:</span>
  <span class="token key atrule">type</span><span class="token punctuation">:</span> mariadb
  <span class="token key atrule">version</span><span class="token punctuation">:</span> <span class="token string">'10.11'</span>
<span class="token key atrule">use_dns_when_possible</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
<span class="token key atrule">composer_version</span><span class="token punctuation">:</span> <span class="token string">'2'</span>
<span class="token key atrule">web_environment</span><span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span>
<span class="token key atrule">corepack_enable</span><span class="token punctuation">:</span> <span class="token boolean important">false</span></code></pre>
<p>Start DDEV with <code>ddev start</code>, then enter the web container using <code>ddev ssh</code>. We'll <a href="https://symfony.com/doc/current/setup.html">install Symfony</a> in the <code>backend</code> directory (at this time of writing the latest version of Symfony is 7.2). We'll have to delete the <code>backend</code> directory first so that Composer can generate the project.</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> <span class="token function">rm</span> <span class="token parameter variable">-rf</span> backend <span class="token operator">&amp;&amp;</span> <span class="token function">composer</span> create-project symfony/skeleton:<span class="token string">"7.2.x"</span> backend</code></pre>
<p>Once installed, create a simple controller to test everything is working as it should:</p>
<pre class="language-php"><code class="language-php"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span>
<span class="token comment">// /backend/src/Controller/HomeController.php</span>

<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span>

<span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Bundle<span class="token punctuation">\</span>FrameworkBundle<span class="token punctuation">\</span>Controller<span class="token punctuation">\</span>AbstractController</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Component<span class="token punctuation">\</span>HttpFoundation<span class="token punctuation">\</span>Response</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Symfony<span class="token punctuation">\</span>Component<span class="token punctuation">\</span>Routing<span class="token punctuation">\</span>Attribute<span class="token punctuation">\</span>Route</span><span class="token punctuation">;</span>

<span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name-definition class-name">HomeController</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractController</span>
<span class="token punctuation">{</span>
    <span class="token attribute"><span class="token delimiter punctuation">#[</span><span class="token attribute-content"><span class="token attribute-class-name class-name">Route</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/'</span><span class="token punctuation">)</span></span><span class="token delimiter punctuation">]</span></span>
    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Response</span><span class="token punctuation">(</span><span class="token string double-quoted-string">"Hello, World!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</span></code></pre>
<p>You can go ahead and point your web browser to <a href="https://tinydev.ddev.site%60">https://tinydev.ddev.site`</a> and you should be greeted with a &quot;Hello, World!&quot; message. If not, don't hesitate to restart DDEV with <code>ddev restart</code>.</p>
<p>At this point, <code>https://app.tinydev.ddev.site</code> is also pointing to Symfony. Let's take care of that.</p>
<h2>Freeze Node's version and install Vue.js</h2>
<p>By default, DDEV comes bundled with Node v22. Let's freeze that so we don't have any bad surprises during our development phase. This can be done very simply inside <code>/.ddev/config.yaml</code></p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">nodejs_version</span><span class="token punctuation">:</span> <span class="token string">'22'</span></code></pre>
<p>Go ahead and restart DDEV (<code>ddev restart</code>) and enter your web container with <code>ddev ssh</code>. We'll now install Vue.js in the <code>webapp</code> directory:</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> <span class="token function">npm</span> create vue@latest</code></pre>
<p>You can choose the flavor you're most familiar with, but the important thing is to specify <code>webapp</code> as your project's name, as this will be the directory used to install the Vue.js app. Then you can install the app itself:</p>
<pre class="language-bash"><code class="language-bash"><span class="token operator">></span> <span class="token builtin class-name">cd</span> webapp <span class="token operator">&amp;&amp;</span> <span class="token function">npm</span> <span class="token function">install</span></code></pre>
<p>Let's point our browser to <code>https://app.tinydev.ddev.site</code> and get... nothing? That's expected. The app is installed, but it's not accessible yet.</p>
<h2>Add a reverse proxy to access the frontend</h2>
<p>To address this issue, let's add an nginx config file in <code>/.ddev/nginx_full/app.tinydev.conf</code>:</p>
<pre class="language-conf"><code class="language-conf">server {

    server_name app.tinydev.ddev.site;

    location / {
        proxy_pass http://localhost:5173;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    listen 80;
    listen 443 ssl;

    ssl_certificate /etc/ssl/certs/master.crt;
    ssl_certificate_key /etc/ssl/certs/master.key;

    include /etc/nginx/monitoring.conf;

    error_log /dev/stdout info;
    access_log /var/log/nginx/access.log;

    include /etc/nginx/common.d/*.conf;
    include /mnt/ddev_config/nginx/*.conf;
}</code></pre>
<p>We'll also have to edit <code>vite.config.ts</code> to allow for this new hostname:</p>
<pre class="language-ts"><code class="language-ts"><span class="token comment">// /webapp/vite.config.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> fileURLToPath<span class="token punctuation">,</span> <span class="token constant">URL</span> <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'node:url'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> defineConfig <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vite'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> vue <span class="token keyword">from</span> <span class="token string">'@vitejs/plugin-vue'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> vueDevTools <span class="token keyword">from</span> <span class="token string">'vite-plugin-vue-devtools'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">defineConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  server<span class="token operator">:</span> <span class="token punctuation">{</span>
    allowedHosts<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'app.tinydev.ddev.site'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  plugins<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token function">vue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">vueDevTools</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  resolve<span class="token operator">:</span> <span class="token punctuation">{</span>
    alias<span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token string-property property">'@'</span><span class="token operator">:</span> <span class="token function">fileURLToPath</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name"><span class="token constant">URL</span></span><span class="token punctuation">(</span><span class="token string">'./src'</span><span class="token punctuation">,</span> <span class="token keyword">import</span><span class="token punctuation">.</span>meta<span class="token punctuation">.</span>url<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>
  <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>Let's now restart DDEV (<code>ddev restart</code>) once more, then hop into the container (<code>ddev ssh</code>) and run <code>npm run dev</code> inside the <code>/webapp</code> directory. Don't forget to run <code>npm install</code> if you haven't already.</p>
<p>If you now point your browser to <code>https://app.tinydev.ddev.site</code>, you should see Vite's default project page.</p>
<p>We now have a working project using Symfony and Vue.js entirely containerized thanks to DDEV! Yay! :tada:</p>
<p>You can now enjoy your web app on <code>https://app.tinydev.ddev.site</code>, and send calls to your API that's residing on <code>https://api.tinydev.ddev.site</code>.</p>
<p>Let's finally add a bit of magic and enhance our Developer Experience thanks to a simple Makefile.</p>
<h2>Enhance the DX thanks to a Makefile</h2>
<p>Having to enter the container to start Vite's web server can get tedious. The same goes for simple Symfony tasks like creating a migration or even emptying the cache, we'd have to change directories and tinker with <code>php bin/console</code> commands. Fortunately, we can use a Makefile as a development toolbox with handy shortcuts:</p>
<p><strong>Important:</strong> Make sure to use tabs (not spaces) for indentation!</p>
<pre class="language-makefile"><code class="language-makefile"><span class="token comment"># /Makefile</span>

WEBAPP_FOLDER<span class="token operator">=</span>/var/www/html/webapp
API_FOLDER<span class="token operator">=</span>/var/www/html/backend

<span class="token target symbol">start</span><span class="token punctuation">:</span>
	ddev start

<span class="token target symbol">stop</span><span class="token punctuation">:</span>
	ddev stop

<span class="token target symbol">up</span><span class="token punctuation">:</span> start
	ddev exec --dir <span class="token variable">$</span><span class="token punctuation">(</span>WEBAPP_FOLDER<span class="token punctuation">)</span> npm run dev

<span class="token target symbol">cache-clear</span><span class="token punctuation">:</span>
<span class="token target symbol">	ddev exec --dir <span class="token variable">$</span>(API_FOLDER) php bin/console cache</span><span class="token punctuation">:</span>clear

<span class="token target symbol">cc</span><span class="token punctuation">:</span> cache-clear

<span class="token target symbol">migration</span><span class="token punctuation">:</span>
<span class="token target symbol">    ddev exec --dir <span class="token variable">$</span>(API_FOLDER) php bin/console make</span><span class="token punctuation">:</span>migration</code></pre>
<p>Keep in mind that you may still want to use <code>ddev ssh</code> for running Composer, as DDEV executes the <code>ddev composer</code> command at the project root by default.</p>
<p>So there you have it! A simple, portable development environment you can use anywhere and share with others.</p>
<p>Happy coding!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Master Eleventy - Part 2: i18n and assets</title>
            <link href="https://www.fabienlasserre.dev/en/blog/master-eleventy-part-2-i18n-and-assets/" />
            <updated>2024-04-11T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/master-eleventy-part-2-i18n-and-assets/</id>
            <content type="html">
                <![CDATA[
                <p>In <a href="/en/blog/master-eleventy-part-1-the-project-structure/">the first part</a> of this series of articles about Eleventy, we've seen the folder structure I tend to use in order to get a healthy and organized workspace.</p>
<p>Here, we'll see how to use Eleventy to get a multi-lingual website working, as well as how to handle JavaScript and CSS assets thanks to its build process.</p>
<h2>Compile JS/SCSS assets</h2>
<p>Let's start with JavaScript and CSS compilation. For that, as we've seen in the first part, I put those files my project's <code>/src/_assets</code> directory. Left as is, those files aren't processed by Eleventy, and are simply ignored during build time. For those to be taken into account, I create a specific file with the extension <code>.11ty.js</code>.</p>
<h3>JavaScript</h3>
<p>For JavaScript, we're going to need the <code>esbuild</code> dependency:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">--save</span> esbuild</code></pre>
<p>We then create the <code>/src/scripts.11ty.js</code> file with the following code:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// /src/scripts.11ty.js</span>
<span class="token keyword">const</span> esbuild <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'esbuild'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token constant">NODE_ENV</span> <span class="token operator">=</span> <span class="token string">'production'</span> <span class="token punctuation">}</span> <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">;</span>

<span class="token keyword">const</span> isProduction <span class="token operator">=</span> <span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'production'</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token keyword">class</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token punctuation">{</span>
  	<span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">eleventyExcludeFromCollections</span><span class="token operator">:</span> <span class="token boolean">true</span>
	<span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">async</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">await</span> esbuild<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  	<span class="token literal-property property">entryPoints</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'src/_assets/js/main.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">bundle</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">minify</span><span class="token operator">:</span> isProduction<span class="token punctuation">,</span>
  	<span class="token literal-property property">outdir</span><span class="token operator">:</span> <span class="token string">'dist/js'</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">sourcemap</span><span class="token operator">:</span> <span class="token operator">!</span>isProduction<span class="token punctuation">,</span>
  	<span class="token literal-property property">target</span><span class="token operator">:</span> isProduction <span class="token operator">?</span> <span class="token string">'es6'</span> <span class="token operator">:</span> <span class="token string">'esnext'</span>
	<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><span class="token punctuation">;</span></code></pre>
<p>Here, we can see one of the great strengths of Eleventy. This file is going to get caught and compiled so that our entry file <code>/src/_assets/js/main.js</code> becomes <code>/dist/js/main.js</code>. And not only will it bring all the needed dependencies but it'll also be minified. Neat!</p>
<h3>Stylesheets</h3>
<p>The principle here is the same for our CSS assets. To that effect, we'll need the <code>sass</code> dependency:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">--save</span> sass</code></pre>
<p>The entry file will be <code>/src/styles.11ty.js</code> with the following content:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</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>
<span class="token keyword">const</span> sass <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'sass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token keyword">class</span> <span class="token class-name">SassTemplate</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token string">'/css/main.min.css'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">render</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> sass<span class="token punctuation">.</span><span class="token function">compile</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">'./_assets/scss/main.scss'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  	<span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'compressed'</span>
	<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>css<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Here again, it's thanks to that <code>.11ty.js</code> extension that the file is getting caught on the fly during build time, and its entry point is <code>/src/_assets/scss.main.scss</code>. The generated file is going to take all the partials and mixins defined in our source file and will end its course in <code>/dist/css/main.min.css</code>, compiled and minified, ready to be used within our templates.</p>
<h2>Internationalization (i18n)</h2>
<p>The JavaScript and CSS assets being handled, we can now take care of the internationalization of our website. Note here that while I'm describing the method I've decided to use, it may not be relevant or the most optimized for your use case. Thanks to its flexibility, Eleventy allows their users to experiment and use the system they wish. The one I've decided to use has several advantages:</p>
<ul>
<li>The <strong>separation of context</strong>: each language gets their own directory.</li>
<li>The <strong>flexibility</strong>: it is quite easy to add new languages.</li>
<li>The possibility to <strong>link pages</strong> and create a &quot;bridge&quot; between languages.</li>
</ul>
<p>So the first thing to do is to separate the different languages in different folders. Thus, we'll get the French content of the website under <code>/fr/</code> and the English version will reside in <code>/en/</code>. This allows for a separation of context, but also to link (or not) two equivalent pages.</p>
<h3>Handle the primary menu</h3>
<p>The first issue we're faced with is the main navigation: since our site uses a layout system, it would be inconvenient to split those between different languages. Once again, it could be possible as Eleventy allows it, but having mutualized templates is the preffered method. So for all the things that are not part of the redactional content, we'll make use of our &quot;database&quot; which resides in <code>/_data/site.json</code>: it is a simple JSON file that we can use how we want. I therefore regroup all the entries for the main menu under the <code>headerLinks</code> key, as such:</p>
<pre class="language-json"><code class="language-json"><span class="token comment">// /src/_data/site.json</span>
<span class="token punctuation">{</span>
  <span class="token property">"headerLinks"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"home"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"about"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/about/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/a-propos/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"projects"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/projects/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/projets/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"blog"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/blog/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/blog/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span><span class="token punctuation">,</span>
	<span class="token punctuation">{</span>
  	<span class="token property">"translationKey"</span><span class="token operator">:</span> <span class="token string">"contact"</span><span class="token punctuation">,</span>
  	<span class="token property">"url"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    	<span class="token property">"en"</span><span class="token operator">:</span> <span class="token string">"/en/contact/"</span><span class="token punctuation">,</span>
    	<span class="token property">"fr"</span><span class="token operator">:</span> <span class="token string">"/fr/contact/"</span>
  	<span class="token punctuation">}</span><span class="token punctuation">,</span>
  	<span class="token property">"external"</span><span class="token operator">:</span> <span class="token boolean">false</span>
	<span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>Under this key, we find an array of objects containin the following keys:</p>
<ul>
<li><code>translationKey</code> is a unique value given to each page. It's thanks to this key that everything works.</li>
<li><code>url</code> specifies for each language the link's destination.</li>
<li><code>external</code> is a boolean which allows to specify if the link has to open in a new tab or not.</li>
</ul>
<p>This allows us to generate the main menu for the website:</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">for</span> <span class="token variable">item</span> <span class="token keyword">in</span> <span class="token variable">site</span><span class="token punctuation">.</span><span class="token variable">headerLinks</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</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>navbar-item <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%</span> <span class="token tag keyword">if</span> <span class="token punctuation">(</span><span class="token variable">page</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token operator">==</span> <span class="token string">"/"</span> <span class="token operator">+</span> <span class="token variable">locale</span> <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token keyword">and</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">translationKey</span> <span class="token operator">==</span> <span class="token string">"home"</span><span class="token punctuation">)</span> <span class="token keyword">or</span> <span class="token variable">page</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token operator">==</span> <span class="token string">"/"</span> <span class="token operator">+</span> <span class="token variable">locale</span> <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token operator">+</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">url</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">"/"</span> <span class="token keyword">or</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">translationKey</span> <span class="token operator">==</span> <span class="token variable">category</span> <span class="token delimiter punctuation">-%}</span></span>is-active<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span><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><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">url</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">i18n</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token variable">item</span><span class="token punctuation">.</span><span class="token variable">translationKey</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endfor</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<p>In this loop, we check if the current page is:</p>
<ul>
<li>The home page <strong>or</strong></li>
<li>A page within a given cagetory <strong>or</strong></li>
<li>A page of a specific category</li>
</ul>
<p>This allows us to specify a CSS class <code>is-active</code>, which will make the current menu entry stand out thanks to a colored border or any other CSS property.</p>
<p>As you can see, we'll make use of the <code>translationKey</code> key we've set before in another part of the menu link:</p>
<p><code>{{ i18n[locale][item.translationKey] }}</code></p>
<p>This makes use of another &quot;database&quot; file, located in <code>/src/_data/i18n.json</code>. This file is the &quot;dictionary&quot; for our site, and we can store there all the strings that need a translation. Here is a simplified example for this file, so that you can see what it looks like:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
    <span class="token property">"fr"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"aboutMe"</span><span class="token operator">:</span> <span class="token string">"À propos de moi"</span><span class="token punctuation">,</span>
        <span class="token property">"twitterShare"</span><span class="token operator">:</span> <span class="token string">"Vous avez aimé cet article ? Partagez-le sur Twitter !"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token property">"en"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">"aboutMe"</span><span class="token operator">:</span> <span class="token string">"About me"</span><span class="token punctuation">,</span>
        <span class="token property">"twitterShare"</span><span class="token operator">:</span> <span class="token string">"Did you like this article? Share it on Twitter!"</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>But before being able to use this dictionary, it's necessary to specify a <code>locale</code> key for each one of our folders <code>/fr/</code> and <code>/en/</code>. To do that, we can use another specific thing about Eleventy: the possibility to define data for a whole directory by using a simple file name trick. The file has to have same name as its containing folder. So we'll create a file named <code>en.json</code> in <code>/src/en/</code> and another <code>fr.json</code> in <code>/src/fr/</code>. The file <code>en.json</code> will contain for example:</p>
<pre class="language-json"><code class="language-json"><span class="token comment">// /src/en/en.json</span>
<span class="token punctuation">{</span>
  <span class="token property">"locale"</span><span class="token operator">:</span> <span class="token string">"en"</span>
<span class="token punctuation">}</span></code></pre>
<p>This little JSON file is the cornerstone for our multilingual support as it allows the use for the <code>locale</code> key I talked about earlier in our templates. This tells Eleventy that everything that resides in this directory has a <code>locale</code> key with a value set to <code>en</code>.</p>
<p>We can now use multiple languages in static pages. For instance, it is now possible to have a page <code>/en/about.md</code> and another <code>/fr/about.md</code>. These two pages have their <code>translationKey</code> set to <code>about</code>, they're &quot;recognized&quot; in the main menu and we can see which page is the current one.</p>
<blockquote>
<p>Note that I'm using the same file name here, but nothing prevents you from using <code>/en/about.md</code> and <code>/fr/a-propos.md</code>. It's simply a convention I've decided to use.</p>
</blockquote>
<p>But what about secondary pages, and blog posts? Let's fix that immediately.</p>
<h3>Handle blog posts</h3>
<p>The first thing to do is to specify a file name <code>blog.11tydata.js</code> (in <code>/en/blog/</code> for instance) and add this code:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// /src/en/blog/blog.11tydata.js</span>
<span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'dotenv'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">config</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<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>

<span class="token keyword">const</span> isDevEnv <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">ELEVENTY_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> todaysDate <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">showDraft</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> isDraft <span class="token operator">=</span> <span class="token string">'draft'</span> <span class="token keyword">in</span> data <span class="token operator">&amp;&amp;</span> data<span class="token punctuation">.</span>draft <span class="token operator">!==</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> isFutureDate <span class="token operator">=</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date <span class="token operator">></span> todaysDate<span class="token punctuation">;</span>
  <span class="token keyword">return</span> isDevEnv <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token operator">!</span>isDraft <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>isFutureDate<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

module<span class="token punctuation">.</span><span class="token function-variable function">exports</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>
  <span class="token keyword">return</span> <span class="token punctuation">{</span>
	<span class="token literal-property property">eleventyComputed</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token function-variable function">eleventyExcludeFromCollections</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">showDraft</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 keyword">return</span> data<span class="token punctuation">.</span>eleventyExcludeFromCollections<span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token function-variable function">hasCover</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">const</span> year <span class="token operator">=</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">.</span><span class="token function">getFullYear</span><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><span class="token punctuation">;</span>
                <span class="token keyword">const</span> month <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">'0'</span> <span class="token operator">+</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">.</span><span class="token function">getMonth</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</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 function">slice</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">2</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><span class="token punctuation">;</span>
                <span class="token keyword">const</span> day <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token string">'0'</span> <span class="token operator">+</span> data<span class="token punctuation">.</span>page<span class="token punctuation">.</span>date<span class="token punctuation">.</span><span class="token function">getDate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">2</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><span class="token punctuation">;</span>

                <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">./src/img/blog/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>year<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>month<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>day<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/cover.webp</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>

                <span class="token keyword">return</span> fs<span class="token punctuation">.</span><span class="token function">existsSync</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span> <span class="token boolean">false</span><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>
	<span class="token literal-property property">layout</span><span class="token operator">:</span> <span class="token string">'blogpost.njk'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">tags</span><span class="token operator">:</span> <span class="token string">'posts_en'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">category</span><span class="token operator">:</span> <span class="token string">'blog'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">permalink</span><span class="token operator">:</span> <span class="token string">'/en/blog/{{ title | slugify }}/'</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>This file is a bit more complex than the simple <code>en.json</code> that we've used earlier, but it allows for several things:</p>
<ul>
<li>The use of the <code>eleventyComputed</code> key which allows us to compute data and return a boolean for certain keys that can then be used inside templates. Here for instance I check the presence of a <code>cover.webp</code> file in the corresponding image folder for the blog post (thanks to its timestamp), so that the template can determine if the post has a cover image or not.</li>
<li>The ability to specify multiple parameters that are shared with every posts, like the layout file to use, but also three very important things: the <code>tags</code>, <code>category</code> and <code>permalink</code> keys.</li>
</ul>
<p>The first one, <code>tags</code>, allows us to specify the name of a collection we'll loop into inside our template. This might seem a bit complicated at first, but once things are in place, the amount of time gained is considerable. This is also why I've decided to go with Nunjucks as my template engine of choice: we can rebuilt the key to use in our template by using the locale defined in the parent directory. Thus, if we need to loop on the posts in the current locale, we can use the following code inside our template:</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">for</span> <span class="token variable">post</span> <span class="token keyword">in</span> <span class="token variable">collections</span><span class="token punctuation">[</span><span class="token string">"posts_"</span> <span class="token operator">+</span> <span class="token variable">locale</span><span class="token punctuation">]</span> <span class="token operator">|</span> <span class="token variable">reverse</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</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><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">post</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">post</span><span class="token punctuation">.</span><span class="token variable">data</span><span class="token punctuation">.</span><span class="token variable">title</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endfor</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<p>Just as a reminder, the use of <code>post.data.title</code> will look inside the front-matter of our post Markdown file.</p>
<p>The second key <code>category</code> allows us to make sure we're in the &quot;blog&quot; section of our website (read the previous chapter).</p>
<p>Finally, the third key <code>permalink</code> allows to generate on the fly a unique permalink, made of the title of the post, correctly formed thanks to the <code>slugify</code> dependency.</p>
<blockquote>
<p><strong>Note:</strong> I'm also using a key called <code>eleventyExcludeFromCollections</code> which allows to see if an article has to be published or not: blog posts marked as drafts (with the <code>draft</code> key set to <code>true</code>) are hidden unless we're in the development environment. This allows us to preview drafts while excluding them from the collection when the site is deployed.</p>
</blockquote>
<h3>Link posts between them</h3>
<p>Thanks to all this, it is now possible to link pages between them with the use of these <code>locale</code> and <code>translationKey</code> keys. For instance, in the template that handles the display of a blog post, here is how I make the connection with the same article in both languages (French and English):</p>
<pre class="language-jinja2"><code class="language-jinja2"><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">if</span> <span class="token variable">locale</span> <span class="token operator">==</span> <span class="token string">'fr'</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">set</span> <span class="token variable">otherLocale</span> <span class="token operator">=</span> <span class="token string">'en'</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">else</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">set</span> <span class="token variable">otherLocale</span> <span class="token operator">=</span> <span class="token string">'fr'</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>

<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">if</span> <span class="token variable">translationKey</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">for</span> <span class="token variable">otherPost</span> <span class="token keyword">in</span> <span class="token variable">collections</span><span class="token punctuation">[</span><span class="token string">"posts_"</span> <span class="token operator">+</span> <span class="token variable">otherLocale</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">-%}</span></span>
        <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">if</span> <span class="token variable">otherPost</span><span class="token punctuation">.</span><span class="token variable">data</span><span class="token punctuation">.</span><span class="token variable">translationKey</span> <span class="token operator">==</span> <span class="token variable">translationKey</span> <span class="token delimiter punctuation">-%}</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>is-translation-link noprint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</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>is-lang-switcher<span class="token punctuation">"</span></span> <span class="token attr-name">data-lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<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><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">otherPost</span><span class="token punctuation">.</span><span class="token variable">url</span> <span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{{</span> <span class="token variable">i18n</span><span class="token punctuation">[</span><span class="token variable">locale</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">'postAlsoAvailable'</span><span class="token punctuation">]</span> <span class="token delimiter punctuation">}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
        <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span>
    <span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endfor</span> <span class="token delimiter punctuation">-%}</span></span>
<span class="token jinja2 language-jinja2"><span class="token delimiter punctuation">{%-</span> <span class="token tag keyword">endif</span> <span class="token delimiter punctuation">-%}</span></span></code></pre>
<h2>Conclusion</h2>
<p>We can now use multiple languages on our website. Translations of editorial contents are directly handled thanks to markdown files in the corresponding folders, and data used for the rest (UI, buttons &amp; co.) are translated thanks to the dictionaries.</p>
<p>So certainly, the configuration might seem heavy at first, and it would've been nice to have something directly integrated within Eleventy. But I believe that's also what makes one of its greatest strengths: it is a static site generator that love to put the developer at the helm, and it lets us find the best (or at the very least the most comfortable) way to solve problems we encounter.</p>
<p>In the next (and maybe last?) part of this series of articles, we'll see how to generate a sitemap and an RSS feed file, as well as a few tips and tricks that'll help us with pages and blog posts.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Master Eleventy - Part 1: The project structure</title>
            <link href="https://www.fabienlasserre.dev/en/blog/master-eleventy-part-1-the-project-structure/" />
            <updated>2024-02-23T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/master-eleventy-part-1-the-project-structure/</id>
            <content type="html">
                <![CDATA[
                <h2>Foreword</h2>
<p>Ever since I started working with interpreted languages, I have to admit that the idea of being able to &quot;compile&quot; a website was surprising to me at first. Nevertheless, something special happens whenever a static site compiles: information is gathered, API calls are made, data passes through, and assets are transformed. And once everything is in place, witnessing all those systems work together like well-lubricated cogs feels immensely satisfying. We are left with a code package that's ready to be delivered, adheres to good practices, and runs extremely fast. Most importantly, the whole thing is as solid as a rock, (almost) impossible to attack, and etched in digital marble.</p>
<p>And yet, the premise is simple: the generator uses Markdown syntax for the editorial content, a templating engine is used to format that content, and finally, a bundle of HTML &amp; CSS files can be deposited on an Apache or Nginx server. No database, no interpreted code, thus making the website ultra-safe and ultra-fast.</p>
<p>Over the years, the website you're currently reading has utilized many of these generators. Let's make a quick rundown of the ones I've had the pleasure to use, and (in my opinion) their strengths and weaknesses.</p>
<h3>Jekyll</h3>
<p>The first one I've used is <a href="https://jekyllrb.com">Jekyll</a>. A precursor to the JAMStack movement, it was created over 15 years ago by Tom Preston-Werner, the founder of GitHub. Today, Jekyll is arguably the most widely used static site generator globally. It's indeed a great system, and its documentation is excellent. However, the downside is that Jekyll requires installing and maintaining a Ruby environment. If, like me, you're not accustomed to this ecosystem, it can become a bit cumbersome.</p>
<h3>Hugo</h3>
<p>I then used <a href="https://gohugo.io">Hugo</a>, often considered Jekyll's &quot;spiritual son&quot;, known for its major strength in extremely short execution time. However, the downside (once again) is that Hugo is written in Go, and its templating syntax is, to put it politely, unique. I never quite got used to its operators or its global syntax. Developing with Hugo was challenging, particularly because its documentation is hard to follow.</p>
<p>That being said, Hugo offers two features I found extremely powerful at the time: its notion of &quot;context&quot; inside template files and its handling of multiple languages. The ability to link content across multiple languages proved to be highly practical and a significant time saver.</p>
<h3>Eleventy (11ty)</h3>
<p>Finally, the current version of my site has been developed using <a href="https://11ty.dev">Eleventy</a>. Once again, its documentation isn't the most practical to read, but it's a project I've been following closely. It benefits from a thriving community and a very &quot;open&quot; approach, which made me want to use it. While i18n support was almost non-existent until a few versions ago, significant progress has been made on this front since then.</p>
<p>One of Eleventy's most attractive features is the fact that it's written in JavaScript. I immediately fell in love with the freedom it offers users. Plus, I believe its name derives from the flexibility of being able to use multiple templating engines.</p>
<blockquote>
<p>Quick note while I'm here: the branding of this project is one of the most awkward things I've encountered. The project is called &quot;Eleventy,&quot; but the domain is spelled &quot;11ty.&quot; Additionally, one of the accepted templating formats gets the extension <code>.11ty.js</code>, while its global configuration file is <code>.eleventy.js</code>. This inconsistency can be quite confusing for newcomers, and I believe the project would benefit from better cohesion around the Eleventy &quot;brand.&quot; It may seem anecdotal, but in 2024, I think it's almost obligatory to attract users, even for an open-source project. You just have to look at other frameworks such as Astro or Laravel to realize how important branding is for the success of such products. One last thing before closing this note: if you're looking for the Twitter account for this project, you'll find it under <a href="https://www.twitter.com/eleven_ty">@eleven_ty</a>. :zany_face:</p>
</blockquote>
<p>Let's revisit this idea of freedom, which can sometimes lead to confusion. While it's undoubtedly a strength for developers, I believe that better organization within the workspace leads to a cleaner and easier-to-browse codebase. That's why, in this first article of a series dedicated to Eleventy, I'll share the way I organize my work to enhance efficiency in my workflow.</p>
<h3>The structure I recommend</h3>
<p>By default, Eleventy makes assumptions about where you may store your data. However, these assumptions can be confusing in their naming and lack cohesion. For example, the folder used for templates is called <code>/_includes</code>, while the one for data is called <code>/_data</code>. While seemingly harmless, this structure can lead to mixing these folders with the final site's tree, as well as with folders used for code that will be manipulated later (such as SASS and JavaScript), along with the project's configuration files.</p>
<p>Therefore, I personally use the following structure:</p>
<pre class="language-txt"><code class="language-txt">/dist
/src
  /_assets
    /js
    /scss
  /_data
  /_layouts
  /en
    /about
    /…
  /fonts
  /fr
    /about
    /…
  /img</code></pre>
<p>And here's the why:</p>
<ul>
<li><code>/dist</code></li>
</ul>
<p><code>/dist</code> is the destination folder for the generated site. By default, Eleventy uses the <code>/_site</code> folder for this purpose. However, as I mentioned earlier, I prefer not to have this folder mixed with others. Therefore, I use <code>/dist</code> instead. It's important to add this folder to your <code>.gitignore</code> file to prevent it from being committed.</p>
<ul>
<li><code>/src</code></li>
<li><code>/src/_assets</code></li>
<li><code>/src/_data</code></li>
<li><code>/src/_layouts</code></li>
</ul>
<p><code>/src</code> becomes the main working folder. It's in this folder that we'll put all the source code for the site to be generated.</p>
<p><code>/src/_assets</code> is a folder reserverd for compiled of minified assets, such as JavaScript or SASS. These files are manipulated during the build process by a specific file, generating their final version (we'll cover this in a future article).</p>
<p><code>/src/_data</code> is the folder that serves as a &quot;database&quot; for the site. Here, we can use JSON files that can be looped over, or we can store the multilingual dictionary. In this case, I don't change the way Eleventy names its files, only their location.</p>
<p><code>/src/_layouts</code> becomes the place where templates are stored. I've chosen to use Nunjucks, but you could use Liquid or another templating engine—it's a matter of personal preference. By default, Eleventy uses a folder named <code>/_includes</code>, but once again, I believe my organization makes more sense, and more importantly, everything is under the <code>/src</code> folder.</p>
<ul>
<li><code>/src/en</code></li>
<li><code>/src/fr</code></li>
<li><code>/src/en/about</code></li>
<li><code>/src/fr/about</code></li>
<li><code>/src/en/...</code></li>
<li><code>/src/fr/...</code></li>
</ul>
<p><code>/src/en</code>, <code>/src/fr</code> and their subfolders are part of the main tree of the compiled site. It's in these folders that we'll put all the different sections of our site, each with their dedicated Markdown content. Since I'm creating a multilingual website, I've decided to use a subfolder per language, as recommended in the documentation. I'll explain in a future article how I can link the content between languages.</p>
<ul>
<li><code>/src/fonts</code></li>
</ul>
<p><code>/src/fonts</code>  is where I store the fonts. I prefer serving them directly instead of relying on Google's CDN. This approach allows me to avoid an external HTTP call and, more importantly, Google's fingerprinting.</p>
<ul>
<li><code>/src/img</code></li>
</ul>
<p><code>/src/img</code> is the folder where all the site's images reside. A quick note while we're here: if you're not already doing so, I encourage you to use recent image formats such as AVIF or WEBP. These formats offer much better compression performance compared to JPG or PNG.</p>
<h3>Compilation helpers</h3>
<p>Deciding on a project structure is one thing, but we also need to inform Eleventy where to find what it's looking for. For that purpose, we have to configure the <code>/.eleventy.js</code> file, which is used during the build process.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// /.eleventy.js</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
	<span class="token literal-property property">markdownTemplateEngine</span><span class="token operator">:</span> <span class="token string">'njk'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">htmlTemplateEngine</span><span class="token operator">:</span> <span class="token string">'njk'</span><span class="token punctuation">,</span>
	<span class="token literal-property property">templateFormats</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'md'</span><span class="token punctuation">,</span> <span class="token string">'njk'</span><span class="token punctuation">,</span> <span class="token string">'html'</span><span class="token punctuation">,</span> <span class="token string">'11ty.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
	<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  	<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
  	<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
	<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>Here we specify that the templating engine we're using is Nunjucks, and the files 11ty has to consider as templates can bear the extention <code>.md</code>, <code>.njk</code>, <code>.html</code> or <code>.11ty.js</code>. These files will go through the Nunjuck engine so that their HTML (or other - specified in the front matter) counterparts are created. Finally, under the <code>dir</code> key, we specify the folders used for input, output, and layouts.</p>
<p>Thus, the project's root directory can contain all the configuration files (<code>.eleventy.js</code>, <code>.gitignore</code>, <code>package.json</code>, etc…), the source code resides in <code>/src</code>, and the generated website will go in <code>/dist</code>.</p>
<p>In a future article, I'll describe how I compile JavaScript/SASS assets, as well as a few tips I use to streamline the development process.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>How to get rid of the Flash Of Unstyled Content</title>
            <link href="https://www.fabienlasserre.dev/en/blog/how-to-get-rid-of-the-flash-of-unstyled-content/" />
            <updated>2020-04-21T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/how-to-get-rid-of-the-flash-of-unstyled-content/</id>
            <content type="html">
                <![CDATA[
                <p>This week I spent some time working on my website's loading performance. I started by switching from <a href="https://kenwheeler.github.io/slick/">Slick</a> to <a href="https://glidejs.com/">Glide.js</a> in order to remove jQuery as a dependency altogether. This helped me reduce the amount of JavaScript and CSS used by half (!). I then added a language preference cookie. Finally, as a simple way to enhance the user experience, I added a function which would make the switch automatically depending on the browser's language settings.</p>
<p>Everything was running smoothly, but I couldn't help but notice that my site was suffering from a <a href="https://en.wikipedia.org/wiki/Flash_of_unstyled_content">Flash Of Unstyled Content</a>, AKA a &quot;FOUC&quot;. It was really noticeable even with the new JavaScript and CSS in place: once a link was clicked, the page would start rendering almost immediately and then the CSS would get applied. This was really annoying as it removes the user from this smooth, almost instant experience I was aiming at. Fortunately, there are a few things we can do to prevent this from happening and get rid of that pesky FOUC.</p>
<h2>Step 1: Hide everything!</h2>
<p>The first thing we want to do is simply to add a CSS instruction so that our body is hidden from the page until it is ready to be unveiled. This allows the page to be fully loaded before we can finally present it to the user. This might be counter-intuitive since we're aiming at speed, and, well, we're <em>slowing</em> things there, but it's a sacrifice we're making for the sake of the user's experience.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</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">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>
  <span class="token comment">&lt;!-- Your awesome website --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre>
<p>We could go with <code>opacity</code> instead, and make use of CSS transitions to add a bit of magic.</p>
<h2>Step 2: Unveil when everything is ready</h2>
<p>We then need to revert that <code>visibility</code> CSS property once the DOM has been loaded and is ready. For that, I'm using a simple helper function, a bit like jQuery's <code>document.ready()</code> method. It calls a callback method once the document is in a &quot;complete&quot; or &quot;interactive&quot; state.</p>
<p>So we simply change the <code>visibility</code> property of my <code>&lt;body&gt;</code> tag to <code>visible</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// Helper function</span>
<span class="token keyword">let</span> <span class="token function-variable function">domReady</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">cb</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  document<span class="token punctuation">.</span>readyState <span class="token operator">===</span> <span class="token string">'interactive'</span> <span class="token operator">||</span> document<span class="token punctuation">.</span>readyState <span class="token operator">===</span> <span class="token string">'complete'</span>
    <span class="token operator">?</span> <span class="token function">cb</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'DOMContentLoaded'</span><span class="token punctuation">,</span> cb<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">domReady</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 comment">// Display body when DOM is loaded</span>
  document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>style<span class="token punctuation">.</span>visibility <span class="token operator">=</span> <span class="token string">'visible'</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 there you go! Our FOUC is gone. With this simple trick, our users get a better experience and don't have a weird mess blinking on their screen before being able to browse our site.</p>
<h2>The Firefox hack</h2>
<p>While things should run smoothly on Chrome, you might still see a flash on Firefox. I struggled to find a solution to this problem until I stumbled upon a bug in Firefox that's been reported <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1404468">3 years ago</a> and is still active. People are still trying to fix this but lucky for us there's a simple hack we can use as a workaround to this problem. Simply add a dummy piece of JavaScript code right after your <code>&lt;body&gt;</code> tag and you should be all set!</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</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">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token number">0</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre>
<p>Pretty neat, huh? Pretty weird also, I must confess. But hey, it does the job.</p>
<h2>Note: Think of the noscript people</h2>
<p>Don't forget that not everybody can or want to execute JavaScript. In that case, this simple line right before our closing <code>&lt;/body&gt;</code> tag will help make our site seen by everybody.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><span class="token selector">body</span> <span class="token punctuation">{</span> <span class="token property">visibility</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span> <span class="token punctuation">}</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span></code></pre>
<p>And we're all set! Now our site should be displayed correctly, without any FOUC. Yay! 🎉</p>
<p><strong>Update - May 1, 2020</strong></p>
<p>It has been reported that my code breaks W3C's code validator. That's because officially, the <code>&lt;style&gt;</code> tag cannot be a child of <code>&lt;noscript&gt;</code>.</p>
<p>To fix this problem, what we can do is remove this <code>&lt;noscript&gt;</code> tag, and add a <code>no-js</code> class on the <code>body</code> element. Then, we simply add this CSS rule in the <code>&lt;head&gt;</code> of the document:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
    <span class="token selector">.no-js</span> <span class="token punctuation">{</span>
      <span class="token property">visibility</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code></pre>
<p>And finally revert this once again right after the <code>&lt;body&gt;</code> tag thanks to JavaScript:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</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">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span></span><span class="token punctuation">"</span></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>no-js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
        document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'body'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">'no-js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre>
<p>This will not only make this W3C compliant, but since we've added a bit of JavaScript in the body of our document, the dummy JS code we added earlier becomes obsolete! So now, everybody's happy, and we can finally grab a fresh glass of water and enjoy the sun.</p>
<p><img src="/img/blog/2020-04-21/sealofapproval.jpg" alt="Seal of approval"></p>
<p><strong>Update 05/06/2021:</strong> Thanks to <a href="https://profitlich.ch/">Moritz Profitlich</a> for correcting a small typo in the source code of this article! 😄</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Speed up your website in 1 minute with Instant.page</title>
            <link href="https://www.fabienlasserre.dev/en/blog/speed-up-your-website-in-1-minute-with-instant-page/" />
            <updated>2019-10-03T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/speed-up-your-website-in-1-minute-with-instant-page/</id>
            <content type="html">
                <![CDATA[
                <p>Behind this clickbait title lies a really nifty little tool. I stumbled upon this while browsing Hacker News a while back. Actually it's been active on this website <a href="https://github.com/fbnlsr/my-website/commit/d862953f35a2ae0992ed11bd8c294bf8d7658a91">since february</a> now but I never took to time talk about it. <strong>Instant.page</strong> is a tiny JavaScript library which is using <em>just-in-time preloading</em> – it preloads an anchor right before a user clicks on a hyperlink.</p>
<p>The technique used if fairly simple: <strong>Instant.page</strong> calculates the time a user spends hovering a link (which is an obvious behavior when you want to click on something) and will start preloading said link if that time exceeds 65ms. Since <a href="https://www.nngroup.com/articles/response-times-3-important-limits/">the average human perceives actions taking less than 100ms as instantaneous</a>, <strong>Instant.page</strong> tricks the brain, hence allowing your users to get a faster, better experience.</p>
<p>The installation is fairly simple, just insert the <code>&lt;script&gt;</code> tag containing the link to the script right before the closing <code>&lt;/body&gt;</code> of your website (which is where all your scripts should reside anyways), and you're all set! <strong>Instant.page</strong> will glue itself automatically to each link on the page. The library is small, a mere 1kB. It's free and open source (MIT).</p>
<p><strong>Instant.page</strong> allows you to control what content should be preloaded or not. You might want to prevent it from preloading some anchors, like logout links for instance. To do that, simply add a <code>data-no-instant</code> attribute to said links and <strong>Instant.page</strong> will ignore them. You can even specify that you want to allow preloading for external links.</p>
<p>Simple, light and easy. If you want to try it yourself, check out <strong>Instant.page</strong> here: <a href="https://instant.page/">https://instant.page</a></p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>SBBT Architecture</title>
            <link href="https://www.fabienlasserre.dev/en/blog/sbbt-architecture/" />
            <updated>2018-09-27T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/sbbt-architecture/</id>
            <content type="html">
                <![CDATA[
                <p>For this second collaboration with <a href="https://nysb.paris">NySB</a>, we worked on the website of <a href="https://sbbt-architecture.com/">SBBT Architecture</a>. Wordpress was the platform of choice, as the client needed a full-fledged admin system so that they could be able to edit pretty much all aspects of the site.</p>
<p>I had to custom-build a slideshow with full-screen images for the home page. The whole thing is managed thanks to Advanced Custom Fields, so that the client can change every aspect of the slides: the background image, the main text, the gradient color and direction, and so on.</p>
<p>For the grid on the projects page, I used <a href="https://vestride.github.io/Shuffle/">Shuffle.js</a>, a responsive grid that one can categorize, sort and filter.</p>
<p>The main issue we encountered was the weight of the site. It makes great use of full-screen images, and since they represent architectural projects, we had to have a good picture quality. I ended up using the on-the-fly API service of <a href="http://reSmush.it">reSmush.it</a>, which compresses the images when an admin uploads an image in the media library.</p>
<p>For this project, we used Wordpress, SCSS, Shufflejs, jQuery and Webpack.</p>
<p>Head over to <a href="https://sbbt-architecture.com">https://sbbt-architecture.com</a> to see the result!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>NySB</title>
            <link href="https://www.fabienlasserre.dev/en/blog/nysb/" />
            <updated>2018-07-31T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/nysb/</id>
            <content type="html">
                <![CDATA[
                <p><a href="https://nysb.paris">NySB</a> is a creative design company based in Paris. For years they've worked with numerous clients, creating visual identity, branding or packaging. They have a solid background in visual design, and are commited to further express their creativity on various digital platforms. Thus, they needed someone to help them build a website that could transpire these ideas. &quot;Be bold, but don't lose yourself in the process&quot;. That was the approach they had when designing their website, and I truly believe the end product is a good representation of what they aspire in their work.</p>
<p>It's been a pleasure working with Thomas and Pierre-Mathieu for this first project. They have a clear image of what the end product should look like, and they are complementary in their ideas and approach. They've trusted me to make their design ideas a reality while listening to the advice I could give them about UX or Responsive Design ideas.</p>
<p>For this project, we used Wordpress, Bulma and Webpack.</p>
<p>Head over to <a href="https://nysb.paris">https://nysb.paris</a> to see what we came up with!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Forestry - A static website CMS</title>
            <link href="https://www.fabienlasserre.dev/en/blog/forestry-a-static-website-cms/" />
            <updated>2018-06-15T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/forestry-a-static-website-cms/</id>
            <content type="html">
                <![CDATA[
                <p>Static websites are getting more and more popular. While some might denigrate this “back to the roots” approach, these systems have numerous advantages. They’re often faster because they’re limited to serving simple web pages, with barely any workload on the serve. The logic is left to be handled by the client, mostly via the usage of APIs, and it’s much easier to put resources in cache and use a CDN during deployment. Besides, these are considered atomic as they allow instantaneous cache invalidation. The website deliverability is therefore 100% assured. Static websites are also often much more secure, as they don’t allow information to be directly sent to the server.</p>
<p>That being said, the update and deployment of a static website has to be made from a machine equipped with the suitable development environment. A terminal, and tools such as Git, Node, NPM, Ruby and others are often necessary. Indeed, the website has to be compiled beforehand or thanks to an automated process such as GitHub Pages or Netlify.</p>
<h2>On the need for a static CMS</h2>
<p>The need for a static CMS therefore makes sense for multiple reasons. First it allows to overcome the need for a development environment. In the case of a blog, the creation of articles becomes much more fluid, and being able to get away from this technical environment allows for a better workflow for the content creation process. Secondly, it allows non-technical users to manage their website and let them be more free in creating and managing editorial content.</p>
<p>Today I’m trying out Forestry, a hosted service which makes use of the GitHub API in order to allow a static website to be remotely managed.</p>
<p><img src="/img/blog/2018-06-15/forestry-logo.jpg" alt="Forestry logo" title="Forestry logo"></p>
<h2>Installation</h2>
<p>The installation of the CMS is extremely simple: I just had to create an account on Forestry, link it with my GitHub account and select the repository for the website in question. I then had to specify if the website is generated by Hugo (which is my case) or Jekyll. Quick note: I had to specify the version of Hugo I’m using, and the ones given by Forestry will not necessarily correspond to yours. It wasn’t a problem for me as I’ve configured Forestry not to generate my website, but it’s something to consider.</p>
<h2>Configuration</h2>
<p>Forestry’s configuration is just as simple. Its system will browse through your repository and generate content types depending on what it finds. All in all, it took me five minutes to have a working back-office and I was able to be productive right away.</p>
<p>It’s even possible to inject the CMS inside your own site and go through a URL you specify (for instance <a href="http://www.example.com/admin">www.example.com/admin</a>), which allows you not to leave your own website.</p>
<h2>Usage</h2>
<p>Forestry works by using GitHub, Gitlab or Bitbucket’s API. It’s a fairly simple CMS to use with a clean interface. On the left, a panel listing all the content types allows access for the different categories of the site, and on the right a Wordpress-like panel displays to the Front Matter fields and the main text editor.</p>
<p><img src="/img/blog/2018-06-15/forestry1.jpg" alt="The admin panel" title="The admin panel"></p>
<p>In my case, Forestry did an incredible job finding all my blog posts and projects, but was a bit limited when it comes to pages content. Thus, for an unknown reason, the system thought my home page and the “About” page were different, and it put them in two different sections.</p>
<p>I was surprised to see that even though Forestry uses GitHub’s API, it’s not possible yet to access a content history.</p>
<p>It’s however possible to rename the file (automatically generated by the CMS) in order to benefit from Hugo’s multilingual support. Thus, by only adding the extension <code>.fr</code> to an existing slug, I was able to let Hugo automatically link two blog posts and suggest the translated version to the visitors.</p>
<p><img src="/img/blog/2018-06-15/forestry2.jpg" alt="Renaming a file" title="Renaming a file"></p>
<p>In my case, a “project” is exclusively configured thanks to the Front Matter. By changing a couple of lines from the config file, I was able to get rid of the text editor and simply have the configuration fields for a project. For Wordpress users, it’s equivalent to having a page constructed thanks to the Advanced Custom Fields plugin, but generated thanks to a YAML file.</p>
<p><img src="/img/blog/2018-06-15/forestry3.jpg" alt="Editing a project" title="Editing a project"></p>
<h2>Pros and cons of the CMS</h2>
<p>The first obvious con of using this type of CMS is the “technical” aspect that remains very strong even in the presence of a graphical interface. Knowing how to name files is a must in order to allow Hugo to handle them, and some aspects of the interface might scare some users.</p>
<p>It would have been smart to allow for a hidden, automatic way of naming content files, thanks to a slug system inside the configuration file, and change the language thanks to a select input.</p>
<p>Adding “users” with restricted permissions is reserved to the “Business” plan which starts at $9 per user per month. In its free version, it’s possible to add “guests” who have almost all rights on the site, which can become pretty problematic.</p>
<p>For instance, you should note that when it comes to Hugo sites, Forestry enforces the TOML format for the Front Matter and config files. In my case, Forestry detected “menus” for my projects, which are not used anywhere. In trying to test this feature and changing the order of this menu, Forestry decided to rewrite 47 files, including Hugo’s config (writing TOML syntax inside a YAML file), which completely broke my build system. I had to use Git to undo these modifications and come back to a previous working version.</p>
<p>On another side note, it is to be considered that Forestry lives in Git, and therefore is going to generate a multitude of commits, that won't certainly respect your commit convention. It's small point, but it's something to think about.</p>
<h2>Wrapping up</h2>
<p>After writing all this, one might seem that I did not enjoy my time with Forestry, but it's far from the truth. While it has some flaws, the team behind it is constantly working on improving it. That being said, and when considering how easy it is to install and use this system, I'd say it’s a really solid choice as a static CMS. It allows to find a certain flexibility when administrating a website while still benefit from the advantages of speed, security and optimizations of a static website generator.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>10 essential extensions for VS Code</title>
            <link href="https://www.fabienlasserre.dev/en/blog/10-essential-extensions-for-vs-code/" />
            <updated>2018-04-19T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/10-essential-extensions-for-vs-code/</id>
            <content type="html">
                <![CDATA[
                <p>I've been using <a href="https://code.visualstudio.com/">Visual Studio Code</a> as my main code editor for more than two years now. I used to work with Sublime Text, which was an amazing software (especially coming from Notepad++), but the guys at Microsoft are constantly doing an amazing job at making their editor the best out there, and their monthly update shows how dedicated they are to keep at improving it.</p>
<p>So it's been my editor of choice, but a good editor would be nothing without good extensions. I've compiled a list of my 10 favorites (plus a few more) extensions I could not live without. They make my day to day work much easier and allow me to save so much time in the long run. Here they are (in no special order):</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=bierner.color-info">Color Info</a></strong></p>
<p>VS Code provides a tiny preview box for colors in CSS files. Color Info allows you to get a much better view of the color your mouse hovers, complete with CMYK or alpha values. It can even act as a color picker, which is really convenient.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/color-infos.png" alt="Color Info"><p class="is-blog-img-title">Color Info</p></div>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=bierner.emojisense">:emojisense:</a></strong></p>
<p>You know <a href="/blog/lets-talk-about-emojis/">I love emojis</a>. This extension allows you to directly insert emojis or type emoji codes in your files. Say you wish to insert a joystick emoji. Just begin to type <code>:joy</code> for instance and you'll get an auto complete window pop up which will allow to directly insert the 🕹 icon. If you type in <code>::joy</code>, it'll insert <code>:joystick:</code> along with a preview of the emoji. It's great!</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=waderyan.gitblame">Git Blame</a></strong></p>
<p>As its name states, this simple extension shows the <code>git blame</code> of the current selected line in the status bar.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/git-blame.gif" alt="Git Blame"><p class="is-blog-img-title">Git Blame</p></div>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one">Markdown All In One</a></strong></p>
<p>I absolutely love Markdown. Actually this whole site makes extensive use of markdown, as it is built with Hugo. This extension helps you write Markdown by adding shortcuts such as <code>Cmd + B</code> for bold text, <code>Cmd + I</code> for italics, and so on. So convenient!</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync">Settings Sync</a></strong></p>
<p>If you're like me and work on multiple (sometimes virtual) machines, this extension is wonderful! It allows you to sync your settings and extensions thanks to a Github Gist that gets downloaded/uploaded.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight">TODO Highlight</a></strong></p>
<p>As the name states, it highlights TODOs, FIXMEs and any keyword you specify. Just write <code>TODO:</code> somewhere and not only will it highlight it, but it'll also list all the ones you've already written anywhere in your project.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/todo-highlight.png" alt="Todo Highlight"><p class="is-blog-img-title">Todo Highlight</p></div>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=octref.vetur">Vetur</a></strong></p>
<p>This extension is a swiss knife for Vue. From autocompletion to snippets, it's a must-have for any frontend developer.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=ban.spellright">Spell Right</a></strong></p>
<p>A multilingual, offline and &quot;lightweight&quot; spellchecker. Spell Right uses your built-in dictionaries to check for errors, and can check for errors in any (and even multiple) language anywhere within your project. Watch out for big files though, as it can sometimes take some time to operate. I usually have it toggled off by default. It's easy to ask it for a spell check just by clicking on the eye icon in your status bar.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig">EditorConfig for VS Code</a></strong></p>
<p>Strangely enough, VS Code does not support <a href="http://editorconfig.org/">EditorConfig</a> by default. Just install this extension and it'll instantly start listening to any <code>.editorconfig</code> file it encounters.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks">Bookmarks</a></strong></p>
<p>Now this one I use it constantly! Bookmarks is an extension that puts little blue bookmarks in your file gutter. It's extremely handy when you need to switch between positions inside a file, or if you need a quick reminder anywhere in your project. I've set up mine with <code>shift + cmd + =</code> (toggle bookmark) and <code>shift + cmd + -</code> (next bookmark) and thanks to this I can jump around files without having to leave my keyboard. The extension also adds a small panel right below your file browser which lists all active bookmarks in the current project. A must-have.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/bookmarks.png" alt="Bookmarks"><p class="is-blog-img-title">Bookmarks</p></div>
<h2>Honorable mentions</h2>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=JerryHong.autofilename">AutoFileName</a></strong></p>
<p>This extension is pretty straightforward. Just type in the beginning of a file/directory and it'll autocomplete its name for you. Really handy when you need to point to a file inside <code>node_modules</code>.</p>
<p><strong><a href="https://marketplace.visualstudio.com/items?itemName=JakeWilson.vscode-cdnjs">cdnjs</a></strong></p>
<p>Most of the time I inject my dependencies in my Javascript files thanks to Webpack. But whenever I need to mockup something quickly, this extension has my back. Using the command palette, you'll be able to insert URLs or script/style tags of all the libraries cdnjs handles. Quite handy.</p>
<div class="is-blog-img"><img src="/img/blog/2018-04-19/cdnjs.gif" alt="cdnjs"><p class="is-blog-img-title">cdnjs</p></div>
<p><strong>Got one to share?</strong></p>
<p>So here are a few extensions I use every day. If you've got one I should check out, hit the comment section or <a href="https://twitter.com/fbnlsr">send me a tweet</a>!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Let&#39;s talk about emojis</title>
            <link href="https://www.fabienlasserre.dev/en/blog/lets-talk-about-emojis/" />
            <updated>2018-01-11T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/lets-talk-about-emojis/</id>
            <content type="html">
                <![CDATA[
                <p>Emojis are everywhere. From Twitter to Facebook Chat, they've grown to become Oxford's 2015 <a href="http://time.com/4114886/oxford-word-of-the-year-2015-emoji/">Word of the Year</a> and even featured in a <a href="https://www.rottentomatoes.com/m/the_emoji_movie">horrendous movie</a>. But what about outside SMS and instant messaging? What about using emojis inside code comments or even git commit messages? Let's find out how we can make the best use of these funny little pictures.</p>
<p>Contrary to what people may think, emojis have been around for quite some time. The first emoji dates back to 1999, and was created by Shigetaka Kurita, a Japanese telecommunication planner for NTT Docomo. At first used solely in Japan, it took those little pictures ten years for some of them to be added to the Unicode character space. Thus, in October 2010 the Unicode Standard 6.0 got released, and with it 722 emojis. They do not live in their own dedicated blocks though and are spread around the Unicode tables. It took years for multiple engineers working at Google and Apple to convince the Unicode Technical Committee to add them. Now emojis are a part of everybody's life.</p>
<p>There are even some quirks and fun little facts about these tiny pictures. For example: emojis can vary from one platform to another. Because of that, the calendar emoji is represented always showing July 17 on Apple products (that date representing the announcement of iCal back in 2002). This led people to &quot;wrongfully&quot; declare July 17 World Emoji Day.</p>
<p>Emojis are also represented differently across platforms, and can be interpreted slighly differently. Take for instance the <code>astonished face</code> emoji. The first one is Apple's interpretation, the second one is Samsung's.</p>
<p><img src="/img/blog/2018-01-11/emoji1.png" alt="emoji1"></p>
<p>Apple's take on this feeling feels a bit more tamed than Samsung's, don't you think?</p>
<p>Other times, it can be the contrary. In this example, Samsung's interpretation of the <code>pouting face</code> feels less &quot;angry&quot; than Twitter's.</p>
<p><img src="/img/blog/2018-01-11/emoji2.png" alt="emoji2"></p>
<p>But enough with the history, let's get down to coding.</p>
<h2>Emojis in git commit messages</h2>
<p>Github has popularized emoji support inside their ecosystem in a <a href="https://github.com/blog/1289-emoji-autocomplete">blog post from 2012</a> thanks to their now famous &quot;<code>:</code>&quot; shortcut. So now, say you want to use the <code>fox face</code> emoji 🦊 somewhere in Github (a commit message, an issue or a gist), you can simply use <code>:fox_face:</code> instead and it will automatically be interpreted by Github.</p>
<p>Using shortcuts is an elegant solution to circumvent emojis not being interpreted. You don't take the risk of breaking something and even if they're not (or badly) rendered, the messages are still readable.</p>
<p>Emojis can also add a lot of clarity to commit messages. Compare these two sequences:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">-</span> Fix editing user not being saved to the database
<span class="token list punctuation">-</span> Cleanup code
<span class="token list punctuation">-</span> Add the ability to edit a user
<span class="token list punctuation">-</span> Fix bad function callback on API request</code></pre>
<p> </p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">-</span> 🐛 Fix editing user not being saved to the database
<span class="token list punctuation">-</span> 📝 Cleanup code
<span class="token list punctuation">-</span> ✨ Add the ability to edit a user
<span class="token list punctuation">-</span> 🐛 Fix bad function callback on API request</code></pre>
<p>You can immediately see where bugs were fixed and where new features were added.</p>
<p>On a platform that doesn't support emojis, this would be read as:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="token list punctuation">-</span> :bug: Fix editing user not being saved to the database
<span class="token list punctuation">-</span> :memo: Cleanup code
<span class="token list punctuation">-</span> :sparkles: Add the ability to edit a user
<span class="token list punctuation">-</span> :bug: Fix bad function callback on API request</code></pre>
<p>Definitely not as fun, but still perfectly readable.</p>
<p>The tech industry as a whole appropriated these shortcuts and <a href="https://www.webpagefx.com/tools/emoji-cheat-sheet/">went far beyond</a> simple emojis. Sure it's nice to use 🐛 to talk about fixing a bug, but try using <code>:trollface:</code> in Slack or Redmine. Boom, you're the new cool kid on the block. Don't use it too often though, you don't want to be <em>that guy</em>.</p>
<p><strong>My advice:</strong> Don't hesitate to use emojis in git commits, but do prefer short-codes. I would also suggest not to go overboard with it and stick to a list of a few ones to denote the major actions (bugfix, new feature, styling, code cleanup, etc..).</p>
<p>If you're not sure were to start or want to suggest a guideline for your team, I highly recommend Carlos Cuesta's <a href="https://gitmoji.carloscuesta.me/">Gitmoji</a>. It even comes with a nifty CLI (simply called <a href="https://github.com/carloscuesta/gitmoji-cli"><code>gitmoji-cli</code></a>) which will help you write your commit messages through an interactive interface. Gitmoji is even used in Atom's <a href="https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages">contribution guideline</a>.</p>
<h2>Emojis in/as code</h2>
<p>Technically, you <em>could</em> use emojis in computer code, but you should be very careful when doing so. Emojis are interpreted as strings in Javascript, but their length can vary.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token string">"🐼"</span><span class="token punctuation">.</span>length         <span class="token comment">// returns 2</span>
<span class="token string">"🇨🇦"</span><span class="token punctuation">.</span>length         <span class="token comment">// returns 4</span></code></pre>
<p>Don't forget emojis can be connected (kind of the same way Fira Code gets you those sexy sexy ligatures). That's how you can now get skin color modifiers (called <code>EMOJI MODIFIER FITZPATRICK TYPE-1</code>, <code>-2</code>, <code>-3</code>, <code>-4</code>, <code>-5</code>, and <code>-6</code>. I'm not kidding). Or even better, if you combine the following emojis: 👨, 👩, and 👧, you get a whole family 👨‍👩‍👧! Let's run that through Javascript.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token string">"👨‍👩‍👧"</span><span class="token punctuation">.</span>length         <span class="token comment">// returns 5</span></code></pre>
<p>Why 5? Because not only do you get the length of each emoji that symbol is made of, but it also uses two <code>ZWJ</code> (Zero Width  Joiner) characters as &quot;glue&quot;. You can even see it in action: copy/paste that emoji inside VS Code for instance, and it'll take you five &quot;arrow key&quot; strokes to go through it.</p>
<p><strong>My advice:</strong> Do not to use emoji in code logic. Plain and simple. But you can still use them in your views. Web browsers have amazing emoji display capabilities, and know how to fallback to a font that <em>will</em> display your &quot;thump up&quot; icon. But watch out and be careful when using an emoji short-code interpreter in those views, especially if you happen to display code blocks on your website. It could trick you, interpreting <code>h:m:s</code> as <code>hⓂ️️s</code>, thus making the code block useless.</p>
<h2>Emojis in code comments</h2>
<p>So what about code comments? Emojis everywhere! As far as I know, you are not susceptible to break anything because of emojis in comments. Modern code editors (Atom, VS Code, Sublime, Intellij...) have amazing emoji support. They even can be pretty useful to make something stand out.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">/**
 * WARNING: Do NOT change this file.
 */</span></code></pre>
<p>Compared with:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">/*
 * 🛑 WARNING: Do NOT change this file.
 */</span></code></pre>
<h2>Final thoughts</h2>
<p>Emojis are a double edged sword. They allow us to express complicated feelings in a quick and fun way. They are the extension of the emoticons we used profusely back in the IRC glory days. They can be used as decorators, adding feeling to an otherwise plain sentence. They also can be used as markers to make something stand out, and even as a complete communication tool when used on their own.</p>
<p>However, since they're not designed and interpreted uniformally across platforms, they can be the source of misunderstandings. Communication relies on the stability of its mean of propagation. If a symbol is changed between the sender and the receiver, the message is not the same. As characters, they also need to be put in context. That's why some of them had to be changed. For instance the <code>:gun:</code> emoji 🔫 which used to be represented by a real gun, is now a water pistol.</p>
<p>When it comes to code though, I'm all in favor of using emojis. Not in the code itself, as I've stated, but rather in comments and commit messages. They embelish the message they're attached to, for they are generally mainly used as pointers. And with the help of short-codes, you can use them without the fear of breaking something.</p>
<p>If you want to know more about emojis, you should check out <a href="https://meowni.ca/">Monica Dinculescu</a>'s work, and especially her talks.</p>
<p>I also recommend Angela Guzman's post on <a href="https://medium.com/@agzmn/the-making-of-apples-emoji-how-designing-these-tiny-icons-changed-my-life-16317250a9ee">the making of Apple's emoji</a>. Angela writes how she and her mentor Raymond designed over 500 emojis during her internship back in 2008. This changed her life, and her work is now in the hands of millions of people.</p>
<p>So go ahead and emoji away, you'll improve readability and break away from the monotony of a dull screen filled with code. 😄</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>My backup strategy</title>
            <link href="https://www.fabienlasserre.dev/en/blog/my-backup-strategy/" />
            <updated>2018-01-03T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/my-backup-strategy/</id>
            <content type="html">
                <![CDATA[
                <p>The protection of digital assets is a multi-million dollar industry. Whether we're talking about military, financial or scientific data, each industry has to be prepared in the event of a loss, and plan for security. They often roll out extreme measures, going as far as having their own (and doubled) dedicated electrical power lines. But what about safeguarding your friend's latest BBQ party pictures? Or your little one's first steps video? Here's how I've learned my lesson from a tragic system failure and what my current setup looks like now.</p>
<h2>A catastrophic failure</h2>
<p>Back in 2008, I made myself a custom NAS (<strong>Network Attached Storage</strong>) using some old computer parts, a bunch of 500 GB hard drives and a copy of <strong>FreeNAS</strong>. The OS ran off a nifty 512 MB IDE flash based drive, and the data array was configured to use <strong>RAID5</strong>. That meant that if a drive was to be damaged, I could always put a new drive in the array and the data would rebuild itself. Note the conditional tense. That's because it worked flawlessly until we moved in 2010. And we stored the NAS in a box next to a speaker with a huge magnet for a month. And two drives failed.</p>
<p>I spent weeks trying to figure out a solution on how to rebuild the data I had lost. But after some time I had to face the reality of things. It was in vain. Years of family photos and videos, an entire MP3 collection, all my video games... It was all lost, forever. My girlfriend was in tears and my &quot;geek pride&quot; after spending all that time planning and building this whole system took a serious hit. I was using hot data as a storage, and at the time I had no backup strategy. Of course I had recovery options, hence the RAID5, but I was not prepared for such a catastrophic failure. And when it comes to computer security, you <em>have</em> to prepare for the worse.</p>
<p>Years later, I learned my lesson. So here is how I handle my digital life now.</p>
<h2>My current setup</h2>
<p>My current setup is mainly built around two things: a new NAS, that I bought and not built, and a backup software that automates how data is handled.</p>
<p>The NAS I'm using now is a <strong>Synology DiskStation DS214se</strong>. It's a very simple machine, running on a 800 MHz dual core CPU, with 256 MB of RAM and two hard drive bays. I put in there <strong>two 2 TB Western Digital Green</strong> hard drives, and configured a single array to be run in <strong>RAID1</strong>: everything that's on one drive gets mirrored to the second one. That means that I lose half of the hypothetical storage space but if one drive fails, I can change it and the data will automatically rebuild itself.</p>
<p>The NAS sits on top of an <strong>APC Uninterrupted Power Supply</strong>. If power is lost in my apartment, the NAS keeps running and I can manually (and safely) shut it down either by using its physical power button (which sends a power off command) or even my phone (my router is also plugged in to the UPS, so even without power I still have internet and network access for a few minutes).</p>
<p>My main backup strategy is handled by an amazing software called <strong>SyncBack Free</strong>. This software allows me to set up various backup scenarios, called profiles. The main profile is a <strong>physical backup</strong> to an external hard drive. When I bought the Synology NAS, I got a third 2 TB drive that is now used as a backup. This is my first failsafe. This is what lacked in my previous setup. Once the backup task is done, that drive is stored <strong>offline</strong> and <strong>off-site</strong>, so it doesn't have to suffer from electrical malfunctions. And even in the event of a fire or flood at my place, my data is safe.</p>
<p>SyncBack then runs two more jobs. Amongst all the data I've lost with that old setup, the loss of family photos were the hardest to cope with. One can always replace music or movies they used to love, as there's a never-ending stream of entertainment to consume. But memories do fade away, and are impossible to retrieve. So I've decided to add another redundancy layer to my backup strategy when it comes to photos and store them online, in my <strong>Google Drive</strong>. SyncBack compares the content of the NAS folder and my Google Drive, and updates the later with the former before performing a <strong>Cyclic Redundancy Check</strong> of each file to see if they are the same on both sides.</p>
<p>I should note that I could use two different apps on the NAS and have it handle these two backups automatically: <strong>USB Copy</strong> and <strong>Hyper Backup</strong>. After trying out both apps in different scenarios, I've decided not to use them as they either store data in a proprietary format (Hyper Backup) or add a bunch of <code>._</code> prepended metadata files to my existing directories (USB Copy). I like the fact that if I ever need to retrieve my files outside of Synology's ecosystem, I still can use a good old <code>cp</code> command to get my files back.</p>
<h2>But wait, there's more!</h2>
<p>So my data is stored on a RAID1 array, and on an offline hard drive. And the photos are backed up online on my Google Drive. I could have stopped there but I thought that it was not enough. Thanks to my <strong>Amazon Prime</strong> subscription, I can upload an infinity number of photos on their <strong>Amazon Drive</strong> cloud service and it won't affect my otherwise limited quota. So hey, let's take this opportunity! Another SyncBack profile backs up the content of my Photos directory to Amazon's servers. I like the fact that my data is stored on two different storage providers. Google and Amazon each have their own infrastructure, so in the event of a failure of astronomical proportions at either one of these places, I <em>may</em> still be safe.</p>
<p>But why stop there? My photos are stored on four different locations now (The NAS, the external hard drive, Google Drive and Amazon Drive). But what about the rest? My music, my documents, my family videos? Well of course they're on the NAS and the external hard drive, but I figured I needed another failsafe. Because so far my backup strategy relies on what could constitute a <strong>single point of failure</strong>: SyncBack. If the software behaves badly or one of my backup profile is not properly configured, I may end up with nothing but a bad save on various locations. I also don't have access to the external hard drive that easily, so if I need to do a backup at any given time, I need to prepare the operation at least a day in advance.</p>
<p>That's why I took a subscription to <a href="https://c2.synology.com/en-us"><strong>Synology C2</strong></a>. It is a fully integrated service that runs natively on <strong>DSM</strong> (<em>DiskStation Manager</em>: Synology's own operating system) and allows me to back up the whole NAS (minus my movies and TV shows, these are not important) to Synology's servers. It uses AES-256 to locally encrypt the data before sending it on the network. I've set it up so it does an automated backup every first day of the week, and then do an integrity check two days later.</p>
<p>I also considered Online's <a href="https://www.online.net/en/c14">C14</a>, as they're really cheap and you can send files over (S)FTP but unfortunately they do not support Synology.</p>
<h2>Room for improvement</h2>
<p>So this is what my current setup looks like now:</p>
<div class="is-blog-img"><img src="/img/blog/2018-01-03/backup-strategy.jpg" alt="My current setup"><p class="is-blog-img-title">My current setup</p></div>
<p>Each file is physically stored on up to 6 different location, with various levels of failsafe measures.</p>
<p>Is this setup perfect? Of course not. First and foremost, it lacks automation. I still have to start each backup task (except for the C2 one) manually, and it is prone to error. I'm working with live data so the array is constantly changing, but this is a backup, not a long term cold storage. And the external hard drive I'm using has to be transported and manipulated, so that's another weak point in the system.</p>
<p>One thing I'll probably change soon is the model of the hard drives I'm using. WD Green are &quot;fine&quot; but they are not made for being used in a NAS. So I think I'll switch them for either WD Red or Seagate Ironwolf line, and probably take the opportunity to do a slight storage upgrade to 3 or 4 TB.</p>
<p>All in all, the main problem with backup strategies is that they're never perfect. Just look at <a href="https://techcrunch.com/2017/02/01/gitlab-suffers-major-backup-failure-after-data-deletion-incident/">what happened at GitLab</a> a few months ago, or even the catastrophic failure that <a href="https://www.theregister.co.uk/2017/07/13/watercooling_leak_killed_vnx_array/">brought OVH to its knees</a> for hours.</p>
<p>One cannot be fully prepared against data loss. Still, I can say that I feel <em>confident</em> with this strategy, and I've tried to think about every scenario (even solar flares, but they're a whole another animal). We'll see how and where my data sits in a few years.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>So long LastPass, hello KeePassXC!</title>
            <link href="https://www.fabienlasserre.dev/en/blog/so-long-lastpass-hello-keepassxc/" />
            <updated>2017-10-06T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/so-long-lastpass-hello-keepassxc/</id>
            <content type="html">
                <![CDATA[
                <p>I've been a fervent advocate of password managers for years. You can ask pretty much anyone in my family or amongst my friends, there was a time where I <em>had</em> to ask the question &quot;By the way, what do you use to store your passwords?&quot;. This was usually followed by a 20 minutes speech about how unsecure their digital life was and a desperate attempt at convincing them that <strong>must</strong> use a password manager. That was, of course, I didn't faint learning that my friend or relative is using the same 7 letters password for absolutely <strong>everything</strong>, &quot;oh and it's <em>hunter2</em>, I don't mind telling you, I have nothing to hide&quot;. Yes, that happened.</p>
<p>For the past four years or so, my password manager of choice has been <a href="https://lastpass.com">LastPass</a>. I've been a happy Premium member ever since, and even though it's had a few hiccups (in <a href="https://blog.lastpass.com/2015/06/lastpass-security-notice.html/">2015</a> and in <a href="https://www.theguardian.com/technology/2017/mar/30/lastpass-warns-users-to-exercise-caution-while-it-fixes-major-vulnerability">2017</a>), they've been pretty transparent about the situation each time and I chose to keep trusting them. I used to log into my account using two-factor authentication: my Master Password and a <a href="https://www.yubico.com/">Yubikey</a>, and it's been working flawlessly for years. However, after a long consideration, I've recently chosen to say goodbye to LastPass and continue my journey with another solution. Here's why.</p>
<h2>A WW1 tank painted pink stays a WW1 tank</h2>
<p>You see, the problem with computer security is that you always have to find the proper balance between how safe you need to be and the convenience of day to day usage. My setup was somewhat secure, but the convenience wasn't there anymore. I usually have to log into my LastPass account several times a day, as I'm using several web browsers, with sometimes several profiles per browser. Therefor, since LastPass automatically logs out whenever a new session is opened somewhere else, I also had to use my Yubikey multiple times per day, even per hour. In the end it was starting to become a burden. Besides LastPass has made some changes to its UI, and is focusing more and more on being as easy to use as possible, and in a way as &quot;opaque&quot; as possible. While I'm all for it, as it draws more and more people to being safer on the web, it can result in really annoying results under the hood. Often, the browser extension does not catch the proper fields for the username/password combo. Or when I'd use the password generator, it didn't necessarily register it properly. It has some difficulties dealing with websites which make use of AJAX requests. It often saves uselessly complicated URLs that are generated during the signup process, making a lot of its database entries dirty. If you're not tech savvy, it's still an amazing product, and it's <strong>way better</strong> than using nothing or that old post-it note that's on your computer screen. But for me, what was once a really good tool to use had become a burden. It was time for something new, or in this case something old.</p>
<h2>Enters KeePassXC</h2>
<p><a href="https://keepass.info">KeePass Password Safe</a> has been around for 13 years. The interface shows its age, but its ease of use and the security features it offers has been proven multiple times. Besides the usual username/password combo, you can specify custom fields, and even attach files to your password database. Speaking of which, KeePass uses AES, TwoFish or ChaCha20 as cipher for the database, and the passwords it contains are protected in memory. It does pretty much everything LastPass does (or the other way around depending on your point of view), albeit with a less fancy UI and a less &quot;automated&quot; process.</p>
<p>The particular flavor of KeePass I'm using is called <a href="https://keepassxc.org">KeePassXC</a>. It's a community-driven fork of the now defunct KeePassX, which was aimed at being a multi-platform version of KeePass. So it works perfectly on Windows, MacOS and Linux. Exporting the database from LastPass and importing it into KeePassXC demands a bit of work, as you have to use a cumbersome CSV file (which you must not forget to destroy!) LastPass export tool provides you with. LastPass' &quot;Secure Notes&quot; are stored in an XML-like format, and you'll probably have to rewrite them manually inside KeePassXC after the import. But after that tedious task is done, using KeePassXC is really easy, and it works flawlessly. Whenever you need to retreive a password, simply switch to the app and hit Cmd+F, type in the first letters of the entry you're looking for and a simple Cmd+C / Cmd+V does the trick. It litterally uses less than five seconds. If you're using a browser extension, it can even automatically fetch your credentials and is therefor as fast as LastPass, if not faster.</p>
<p>The setup I'm using now is as follows. The password database file is stored on my Google Drive. This allows me to have a silent synchronization between my different devices, and provide me with an online backup. I'm using both my Master Password (with a 100+ bits entropy) and a key file (which is stored locally, <strong>not</strong> on Google Drive) to unlock the database. It can communicate automatically with my various browsers thanks to two extensions: chromeIPass and PassIFox. The database file and the key file are backed up on a Network Attached Storage drive, and two offline copies.</p>
<h2>Is everything right?</h2>
<p>So I'm finally happy again. The main &quot;problem&quot; I have with KeePassXC is its UI. In these days of Flat UI and Material Design guidelines, the software feels really dated. It also lacks some basic features such as showing which fields can be displayed in the entries columns, and my biggest gripe: custom fields do not show straight away when you search for a specific entry. Let's say for instance you create an entry for a credit card. It contains several custom fields such as the card number, its expiration date and the CCV. KeePassXC won't show those fields unless you go and edit the entry to display the custom fields, or you right click and select one of the &quot;copy attributes&quot; options. It would have been much better if each entry could be displayed in its entirety on a single pane, minus the protected fields of course.</p>
<p>I should give an honorable mention to <a href="https://keeweb.info/">KeeWeb</a>, a web app (available as an Electron desktop app as well), which tries to rejuvenate KeePass in that regard. But it poses even more security problems than KeePass does. I won't go into too much details in this post, but the flaws are easy to guess.</p>
<p>All in all, I feel much better using KeePassXC than I was using LastPass. Of course, this solution is not the most secure there is, especially by storing the database file on the cloud. I could fix this issue by using a self-hosted cloud service like OwnCloud or Bitorrent Sync. But once again, the balance between security and day to day usage would have been lost. I'd love for KeePassXC to support TOTP so that I could use again my Yubikey and get rid of the keyfile, but so far I feel confident in that solution.</p>
<p>By the way, what do you use to store your passwords?</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Push notifications: kill the noise</title>
            <link href="https://www.fabienlasserre.dev/en/blog/push-notifications-kill-the-noise/" />
            <updated>2017-09-26T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/push-notifications-kill-the-noise/</id>
            <content type="html">
                <![CDATA[
                <p>Ever since last summer, the first thing I do whenever I install a new app on my phone is to go into its settings and completely turn off push notifications.</p>
<p>When Apple came up with push notifications back in 2009, they were a godsent. No longer did we have the need to check our phone to see if a new email had arrived! Whenever something important happened, a subtle <em>Ding!</em> or a quick vibration alterted us of an incoming tweet or appointement. It was a great way for the user to stay in touch with their digital world.</p>
<p>The problem is that now, each and every app thinks it's the most important thing and you HAVE to listen to what they say. Congratulate Helen for her new job! Marc is tweeting about #takeaknee! Don't forget to check on your Happy Dragon's Eggs! Tom has posted for the first time in a while! It has become so dominant that now it feels like notifications are nothing but noise. So much so that people have started to develop new diseases such as <a href="https://en.wikipedia.org/wiki/Phantom_vibration_syndrome">Phantom Vibration Syndrome</a>. How often do you think you get a new notification and reach for your phone, but it turns out that it was just your brain tricking you. There are even people who are experiencing anxiety related to notifications. The constant flow of information has become overwhelming, and it just won't stop. And now, you can also get push notifications from websites. While I love the technology behind it, it's unfortunately so badly used by web developers around the globe. Each and every website wants your authorization to notify you about the latest trend in whatever they're talking about. It's presented as a way to deliver a better service, but let's not kid ourselves: it exists for the solely purpose of making you come back there, and present you with just a few more ads.</p>
<p>Last year I bought myself a Pebble. It's a great little gadget that goes around my wrist and vibrates whenever I get a notification. Thanks to it, I can't miss a single email, phone call or text. It's also nice because it's discreet. It's been a year since my phone last chimed, and now that it's always on silent mode, it doesn't bother my coworkers when I'm in an open space, or my family during our sunday lunch. I've started to look less at my phone, because since it's connected to my watch thanks to Bluetooth, I won't miss a notification as long as the phone is less than 10 meters aways from me. But I've realized that it was at my own expense. The problem is that I started the bad habit of constantly having to look at my watch. Whenever my phone needs my attention, I get these three taps on my wrist that remind me it's there, and it needs me to take care of it. While fun at first, it started to feel like a dog collar, and that Bluetooth link started to feel more like a leash than a usefull tool.</p>
<p>So I've decided to turn it all off. The only notifications I've kept on my phone are Emails, and everything that's <em>directly</em> aimed at me (Telegram, Twitter DMs and SMS). That's it. LinkedIn is quiet, Instagram as well, and I'll check on my Amazon deliveries whenever I damn please. On my desktop, I've disabled all notifications as well. Now I take a glimpse at my Dock every once in a while, and whenever I see a Slack or Airmail badge, I'll see what it's about only if I have nothing more important to do. I often completly shut down email and open it right before lunch, or a moment before leaving the office. I won't interrupt a coding session just to see if my Aliexpress order has shipped or if someone has replied to a Github PR. And it feels wonderful.</p>
<p>Turning off notifications has been a great thing to do. I feel like I'm more in control over my digital life. I get less interrupted by all this noise, and in the end much less stressed about this constant flow of notifications. I feel happier, but not less &quot;connected&quot;, I just chose to open an app because I feel like it, and not because an algorithm has decided it's a good time for me to do so. A <a href="https://www.wired.com/story/turn-off-your-push-notifications/">recent article from Wired</a> reports that in 2013, &quot;<em>Apple proudly announced [...] that 7.4 trillion push notifications had been pushed through its servers</em>&quot;. Where is the limit between information and spam?</p>
<p>I hope this trend will develop and people start cutting off notifications more and more. Unfortunately, now that Apple has added SIM support for its Apple Watch, the &quot;notification machine&quot; has become independant, so people will get even more reminders to check on their phone. My advice is try it for a week, and see how much you miss having a device constantly buzzing in your pocket. I bet you'll feel much better, even after day 3.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>18-55 Productions</title>
            <link href="https://www.fabienlasserre.dev/en/blog/18-55-productions/" />
            <updated>2017-02-21T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/18-55-productions/</id>
            <content type="html">
                <![CDATA[
                <p>Last week we launched the new website for <strong><a href="http://18-55.fr">18-55 Productions</a></strong>!</p>
<p><strong>18-55</strong> is a video production company, based in Bordeaux. They work with various brands such as <strong>DC Shoes</strong>, <strong>Orange</strong>, or even <strong>Electronic Arts</strong> amongst many others. Their goal was to get a website that could put a focus on the content (their productions) and at the same time show that they also act as a platform for different type of professions revolving around their line of work. That's why they're surrounded by various artists, photographers, writers and videographers which allow them to quickly put up a team of talented people for the projects they're working on.</p>
<p>Their second desire was to get a back-office which would allow them to add/remove content quickly and easely. Wordpress was the obvious choice, and together we came up with a minimalistic design based on a grid of squares which allowed them to reorder the whole site as they please. They were immediately thrilled by this approach and after a few exchanges between their CEO, the Production Manager and myself we agreed on this design. All in all I'm really happy with what we came up with and so far the feedback after launch is rather positive.</p>
<p>Thanks a lot to <strong>18-55</strong> for their time and dedication to this project!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>My hectic sleep pattern</title>
            <link href="https://www.fabienlasserre.dev/en/blog/my-hectic-sleep-pattern/" />
            <updated>2017-02-07T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/my-hectic-sleep-pattern/</id>
            <content type="html">
                <![CDATA[
                <p>A couple of months ago I bought myself a <a href="https://www.pebble.com/">Pebble</a>. It's a nifty little device which acts as an extension to my phone, directly strapped to my wrist. Thanks to it I can quickly send pre-recorded messages with the press of a few buttons, but most importantly get notifications on new messages, them being emails, SMS or even Telegram messages. Which can get a bit invasive especially when my friend Edouard spams me with Wu-Tang Clan lyrics. All in all, it's really an amazing gadget and I'm really happy with it.</p>
<p>Another cool feature is its ability to track sleeping patterns. Thanks to its built-in gyroscope, it can detect wrist movements and deduce if I'm sleeping or not. Granted, it's not 100% bullet-proof, but it does a good job at giving a broad idea of how good (or bad) my sleeping habits are. And boy they are terrible.</p>
<p><img src="/img/blog/2017-02-07/sleeping-patterns.png" alt="My sleep pattern for the past week"></p>
<p>According to my Pebble, I sleep 7 hours 3 minutes per night on a 30 days average, with an average deep sleep duration of 1h 48m. It's not <em>that</em> bad, but when you take into account that during a same week I can sleep from 3h 55m one day to 9h 18m another, there's clearly something really wrong with my sleep schedule.</p>
<p>My usual &quot;worst&quot; night is the one from sunday to monday, with an average of 5 hours of sleep. My &quot;best&quot; night is usually the one from saturday to sunday, with up to 11 hours (!) of sleep.</p>
<p>The average human needs <a href="https://sleepfoundation.org/sites/default/files/SleepTimeRecommendations012615%5B1%5D-page-001_0.jpg">between 7 and 8 hours of sleep</a> for optimal functions, which means I have a <a href="http://www.health.harvard.edu/staying-healthy/repaying-your-sleep-debt">sleep debt</a> of about 30 minutes to an hour <em>per day</em>. That's a cumulated debt of about 4-5 hours per week. On the long run, such deprivation is the cause of stress, weight gain, fatigue, and even diabetes. If you need more info on the benefits of sleep, I highly suggest you <a href="https://pillowpicker.com/health-wellbeing/benefits-of-sleep/">read this article</a> from Pillow Picker.</p>
<blockquote>
<p>&quot;But Fabien, all you need to do is go to sleep one hour earlier, that's all!&quot;</p>
</blockquote>
<p>Yeah, that's not taking into account the absolutely amazing capacity of the human brain to slack off.</p>
<p><img src="/img/blog/2017-02-07/chuck-and-beans.jpg" alt="Coffee is good"></p>
<p>Getting more sleep has been a goal of mine for 2017. I've already taken some steps towards this, removing my tablet from the nightstand and replacing it with <a href="https://www.amazon.com/dp/1421586207/">a good book</a>. But I still need to slap myself on the wrist and stop spending my evenings reading about code or cruising around <a href="https://en.wikipedia.org/wiki/The_Elder_Scrolls_V:_Skyrim">Tamriel</a> or <a href="https://en.wikipedia.org/wiki/Counter-Strike:_Global_Offensive">Dust2</a>.</p>
<p>I hope to write an addendum to this post soon, describing how my sleep habits have changed, if I ever manage to change them. In the mean time, I'm off to take a nap.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Gitlab crashed, who&#39;s to blame?</title>
            <link href="https://www.fabienlasserre.dev/en/blog/gitlab-crashed-whos-to-blame/" />
            <updated>2017-02-02T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/gitlab-crashed-whos-to-blame/</id>
            <content type="html">
                <![CDATA[
                <p>Last night, a &quot;tired sysadmin&quot; ran a directory wipe on the wrong server of code hosting platform Gitlab, deleting over 300 GB of live production data. Following that incident, the website was taken offline while they were trying to restore from a backup. Plot twist: &quot;none [of their] backup/replication techniques [were] working reliably or set up in the first place.&quot; It took them a full day to fix the damages, leaving thousands of devs stranded, and losing close to 6 hours of data.</p>
<p>On Twitter, Gitlab posted the most amazing and devastating tweet a sysadmin could ever write in their career:</p>
<blockquote>
<p>We accidentally deleted production data and might have to restore from backup.</p>
</blockquote>
<p>To be honest, there couldn't be a more disastrous catastrophy for a company that's relying on data as much as they do. Me and the team I'm currently working with have been using Gitlab for a few months now, and already had to suffer from their constant downtime. At one point, <a href="http://Gitlab.com">Gitlab.com</a> could be offline three to five times a day.  We were glad to hear that they were looking to <a href="https://about.gitlab.com/2016/12/11/proposed-server-purchase-for-gitlab-com/">leave the cloud</a>, only to learn that finally, they'd be looking to expand in the cloud after all.</p>
<p>As Hacker News user <em>connorshea</em> pointed out:</p>
<blockquote>
<p>it's never the fault of a person, always of a system</p>
</blockquote>
<p>This could be applied to what happened at Gitlab. It feels amazing that for such critical tasks (you don't delete a whole database directory every day), they don't rely to the most simple and effective management system: checklists. It is used around the globe whenever a system is at risk. Airline pilots, surgeons and nurses, army crew, they all have checklists. It is the safenet against repetitive and menial tasks. That's why you'll see airline pilots talking through everything they're doing during an emergency procedure, with their co-pilot enforcing what they're about to do. It usually goes like this:</p>
<blockquote>
<p>– &quot;I'm about to turn off A/C.</p>
<p>– Green for turning off A/C.</p>
<p>– A/C is turned off.</p>
<p>– Confirmed. A/C is turned off&quot;.</p>
</blockquote>
<p>This is greatly exagerated as I haven't flown a plane in.. well ever. But you get my point. Each and every step is monitored and confirmed by a fellow crewman. If you want to know more you can read this Wikipedia article about <a href="https://en.wikipedia.org/wiki/Crew_resource_management">crew resource management</a>.</p>
<p>Gitlab has taken the stance of total transparency, going as far as live-broadcasting their backup process. Many praise them for taking such a stance, but they hardly couldn't take another one, as there was a slight possibility of irrecuperable failure.</p>
<p>At the end of the day, the real question is not really &quot;<em>who</em>'s to blame?&quot; but rather &quot;<em>what</em>'s to blame?&quot;. Poor practices, lack of preparation and a frustrated and tired engineer make for a bad combo. I'm sure Gitlab will grow from its mistakes. I'm not so sure they've handled the situation perfectly and are not losing clients by the minute, but hey, we can't blame them from trying not to go under.</p>
<p>In rememberance for this day, <a href="https://twitter.com/CyberShambles">@CyberShambles</a> has dedicated February 1st as <a href="http://checkyourbackups.work/">Check Your Backup Day</a>. Good one.</p>
<hr>
<p>Source: <a href="https://www.theregister.co.uk/2017/02/01/gitlab_data_loss/">https://www.theregister.co.uk/2017/02/01/gitlab_data_loss/</a></p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>The table naming dilemma: singular vs. plural</title>
            <link href="https://www.fabienlasserre.dev/en/blog/the-table-naming-dilemma-singular-vs-plural/" />
            <updated>2017-01-25T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/the-table-naming-dilemma-singular-vs-plural/</id>
            <content type="html">
                <![CDATA[
                <p>The other day, while in a planning poker session, the question of the naming of a particular table arose. During that conversation, one of our developers suggested that the table shall have a singular name, while others questioned that idea and thought that every table names should be plural. This led me to ask this question: is there a better choice? <em>Should</em> table names be singular or plural?</p>
<p>It's funny to see that despite the fact that there is no clear convention regarding this topic, it is a question that gets asked pretty often, and it is the source of big debates in the DB world.</p>
<h2>Using plural names</h2>
<p>Narayana &quot;Vyas&quot; Kondreddi (long-time DBA and SQL Engineer) wrote back in 2001:</p>
<blockquote>
<p>Tables represent the instances of an entity. For example, you store all your customer information in a table. Here, 'customer' is an entity and all the rows in the customers table represent the instances of the entity 'customer'. So, why not name your table using the entity it represents, 'Customer'. Since the table is storing 'multiple instances' of customers, make your table name a plural word.</p>
</blockquote>
<p>It feels logical, and somewhat &quot;natural&quot;. You store several customers inside a table (those &quot;multiple instances&quot;), so the table should naturally be named <code>customers</code>. It also makes sense when writing an SQL statement. When you want to go through all your customers, you <code>SELECT * FROM customers</code>...</p>
<p>When using plural name, one can consider a table like a crate containing several items. A crate of apples should be labelled <em>Apples</em>, whether it contains one or a hundred apples.</p>
<h2>Using singular names</h2>
<p>The people that advocate the usage of singular names often cite the fact that when pointing at a database record, it feels confusing to use a plural name to describe a single item. Thus, why should you use <code>clients.name</code> when you're pointing only at one client?</p>
<p>This is enhanced when writing SQL statements, that suddenly appear more natural. For exemple, <code>SELECT activity.name</code> feels better than <code>SELECT activities.name</code></p>
<p>It also avoids confusion of english pluralization which can sometimes be tricky for non-fluent writers. e.g. <em>activity</em> becomes <em>activities</em> while <em>person</em> becoms <em>people</em> and <em>data</em> remains <em>data</em>...</p>
<p>When using singular names, one can consider using the &quot;set theory&quot; to tables, meaning that any instance in the set is representative of the set, so <code>apple</code> is an Apple set. It is agnostic of how many apples are in the set.</p>
<h2>What should you use?</h2>
<p>I personally do believe that a database table is the representation of a collection of items. As such, it should be named using a plural word, since it stores several copies of an entity. Thus, each <code>user</code> is stored inside the <code>users</code> table.</p>
<p>The only thing that could have made me consider using singular names is the SQL statement point, as it feels less natural to use a plural name for a query on a single item. If you're bugged out by that point you can still use this simple trick to circonvert that issue:</p>
<pre class="language-SQL"><code class="language-SQL">SELECT id, name, description FROM products product WHERE product.name = 'foo' AND product.description = 'bar'</code></pre>
<p>It's even more practical when dealing with more complex pluralization:</p>
<pre class="language-SQL"><code class="language-SQL">SELECT id, name, description FROM activities activity WHERE activity.name = 'foo' AND activity.description = 'bar'</code></pre>
<p>If you're really concerned about writing good looking code that feels like proper english, my advice is to use plural names for tables, but single names for the corresponding entities. As such, for e.g. in Symfony you'd declare an entity <code>Activity</code> as such:</p>
<pre class="language-php"><code class="language-php"><span class="token comment">/**
 * @Activity
 *
 * @ORM\Table(name="activities")
 */</span>
<span class="token keyword">class</span> <span class="token class-name-definition class-name">Activity</span>
<span class="token punctuation">{</span>
<span class="token operator">...</span>
<span class="token punctuation">}</span></code></pre>
<p>The table <code>activities</code> stores all instances of the possible object <code>Activity</code>. Thus, when invocating and working with that object it makes sense to use the syntax <code>$activity = new Activity()</code> and <code>$activity-&gt;setName(&quot;foo&quot;)</code>.</p>
<h2>In short</h2>
<p>If I had to give you one piece of advice though, it would be to this: <strong>be consistent</strong>. Stick to whatever convention you've decided, and apply it throughout your project. Use what feels the most natural to you and your team. Having the ability to rely on convention is often a good thing, but there are times when a convention is not needed. What's the most important is to make a clear choice and adopt it. Of course, it's also important to keep an open ear to the arguments people could provide that'd make you divert from that choice.</p>
<p>If you're still looking for some guidance, here are some other tips you might want to use when naming database tables:</p>
<ul>
<li>Use short names for your tables</li>
<li>Use underscore to separate words (no spaces or camelCase), for e.g. <code>product_dimensions</code></li>
<li>Be somewhat descriptive: a table name should represent its content. It's easier to read <code>client_bills</code> rather than <code>clb</code></li>
<li>Use unique names that cannot collude with SQL/RDBMS reserved words (avoid <code>name</code>, <code>order</code>, <code>percent</code>...)</li>
<li>Do not use the table name followd by &quot;id&quot; (e.g. <code>client_id</code>) as your PK. <code>id</code> is more than enough and everyone will understand</li>
<li>Use <code>id</code> and not <code>ID</code></li>
<li>Actually, never use capital letters in your table or field names. Ever.</li>
</ul>
<hr>
<p>Sources:</p>
<ul>
<li><a href="http://stackoverflow.com/questions/338156/table-naming-dilemma-singular-vs-plural-names">http://stackoverflow.com/questions/338156/table-naming-dilemma-singular-vs-plural-names</a></li>
<li><a href="http://stackoverflow.com/questions/7899200/is-there-a-naming-convention-for-mysql">http://stackoverflow.com/questions/7899200/is-there-a-naming-convention-for-mysql</a></li>
<li><a href="http://vyaskn.tripod.com/object_naming.htm#Tables">http://vyaskn.tripod.com/object_naming.htm#Tables</a></li>
<li><a href="https://justinsomnia.org/2003/04/essential-database-naming-conventions-and-style/">https://justinsomnia.org/2003/04/essential-database-naming-conventions-and-style/</a></li>
</ul>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>HTTPS all the things!</title>
            <link href="https://www.fabienlasserre.dev/en/blog/https-all-the-things/" />
            <updated>2017-01-24T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/https-all-the-things/</id>
            <content type="html">
                <![CDATA[
                <p>Starting with Chrome 56 (which should come out in a few days), Google will report websites that collect passwords or credit card numbers as non-secure if they are served through HTTP.</p>
<p><img src="/img/blog/2017-01-24/chrome56.png" alt="Chrome 56 showing a non-secure page served through HTTP." title="Chrome 56 showing a non-secure page served through HTTP."></p>
<p>This is a huge deal, as it will enforce pretty much everyone to switch to HTTPS. The goal of Google has always been to provide a better and more secure web for the users. I believe that it's a good thing that such a giant is taking steps to enforce such measures. But the most important thing is not to train web developers to serve secure pages, but rather train users not to trust non-secure websites. This is why Chrome will gradually enhance the &quot;Not Secure&quot; notification, so that it appears with a red triangle, like the &quot;broken HTTPS&quot;.</p>
<p>HTTPS is getting cheaper and cheaper, and with things like <a href="https://letsencrypt.org/">Let's Encrypt</a>, there is soon going to be less and less excuse to serve non-secure websites. Which leads me to think I also should add a certificate to my own site.</p>
<hr>
<p>Source: <a href="https://security.googleblog.com/2016/09/moving-towards-more-secure-web.html?m=1">Google Blog</a></p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Hey look, I have a blog!</title>
            <link href="https://www.fabienlasserre.dev/en/blog/hey-look-i-have-a-blog/" />
            <updated>2017-01-23T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/hey-look-i-have-a-blog/</id>
            <content type="html">
                <![CDATA[
                <p>Behind this smart title is a reminder to myself that I should start posting more. Writing about the web and its technologies has been something I've always wanted to do but I've been so focused on work lately that I completely forgot about it. Or at least I completely forgot about taking the time to do it.</p>
<p>I'm still considering whether I should cross-post the articles I'll be writing here on <a href="https://medium.com/@fbnlsr">Medium</a> or not. All the cool kids are using this tool, maybe I should too. I've <a href="https://medium.com/@fbnlsr/the-three-pillars-of-a-developer-s-mind-ab4be1d93d99#.ksxafk9g5">written a piece</a> there, which has had quiet a good feedback, so I might post more about my thoughts regarding the &quot;mind&quot; behind web development (and web developers).</p>
<p>This first post of 2017 could be a great way to wish everyone a happy new year! Let's hope that this year will be as awesome as possible for everyone. It's been pretty good so far for me, and I can't wait to see what's coming in the following months.</p>
<p>Since last month I've been learning Symfony. I have to admit that I had some presumptions towards this framework, not really knowing why. I had heard it was hard to learn, badly written and all in all confusing. I am really surprised to see that it is in fact really well put together and pretty easy to learn (to some extent of course). I can't wait to put what I've been learning to practice and really start working with it.</p>
<p>I've also been looking at vue.js with a real interest. It looks so slick and powerful! I already have an idea of a cool app I could put together that'd make use of both technologies. But hey, the <a href="http://www.eleague.com/major/">E-League Major</a> has started yesterday, so no time for that yet. :)</p>
<p>Anyways, onwards to 2017 and let's hope I'll get some time to fill these columns sooner than later!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Healthy Warriors</title>
            <link href="https://www.fabienlasserre.dev/en/blog/healthy-warriors/" />
            <updated>2016-08-31T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/healthy-warriors/</id>
            <content type="html">
                <![CDATA[
                <p>I had the opportunity to work once again with <a href="https://www.ligne13.com/">Ligne 13</a>, this time for Healthy Warriors!</p>
<p>Healthy Warriors is a yoga center and café in Paris, regrouping several yoga teachers from around the globe. It has been created by Aria Crescendo and Gus Forristal, and features guest teachers and seminars throughout the year.</p>
<p>The website in itself is fairly simple, and is supported by Wordpress. A few custom post types and custom pages had to be made in order to give the client the freedom to customize the articles available in the shop as well as dishes and drinks served at the bar.</p>
<p>Thanks again to <a href="https://www.ligne13.com/">Ligne 13</a> for this project!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Agence Saint Germain</title>
            <link href="https://www.fabienlasserre.dev/en/blog/agence-saint-germain/" />
            <updated>2016-07-07T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/agence-saint-germain/</id>
            <content type="html">
                <![CDATA[
                <p>I’ve developed the new website for <a href="http://www.asgparis.com/">Agence Saint Germain</a> in collaboration with <a href="https://www.ligne13.com/">Ligne 13</a>. Agence Saint Germain is an agency for photographers, make-up artists, stylists and set designers. Their goal is to promote and sell the services of the artists that are signed with them.</p>
<p>It was a really interesting project, as the technical challenge was pretty special. For this site, I had to make use of Masonry, while allowing the website to be able to manage several picture sizes and orientations, have a parallax effect, and be responsive. The task was not the simplest, but I’m happy with the result.</p>
<p>The whole thing is backed by Wordpress. The client had a precise idea of the kind of experience he wanted for his users, and I find the end result very interesting. The use of white space gives all its importance to what matters: the pictures. A custom made lightbox was added, which allows people to select photos and save for later.</p>
<p>Thanks a lot to <a href="https://www.ligne13.com/">Ligne 13</a> for this project !</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>CFA Le Vigean</title>
            <link href="https://www.fabienlasserre.dev/en/blog/cfa-le-vigean/" />
            <updated>2016-05-18T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/cfa-le-vigean/</id>
            <content type="html">
                <![CDATA[
                <p>Print and Web Agency ComTogether has asked me to develop the new website for <a href="http://cfa-levigean.fr">CFA Le Vigean</a>, a formation center for students in various fields (dentistry, woodworking, geomatics engineering…).</p>
<p>The home page was designed internally by the agency, and the client was confident enough to trust me for the layout of the rest of the site, as well as the UX approach for the responsive design.</p>
<p>The base has been done using Wordpress, and several Custom Posts had to be made for the website not only offers students a place where they can gather informations regarding the various majors the school offers, but it’s also a place for employers and graduates to exchange.</p>
<p>Thanks to ComTogether for this project.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>The three pillars of a developer&#39;s mind</title>
            <link href="https://www.fabienlasserre.dev/en/blog/the-three-pillars-of-a-developers-mind/" />
            <updated>2016-03-11T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/the-three-pillars-of-a-developers-mind/</id>
            <content type="html">
                <![CDATA[
                <p>If I had to imagine the worst way I could do my job, I would describe it as sitting behind a desk, spewing out lines after lines of code. Clock in at 9, clock out at 5, and wait for the next day to come. I guess that’s one way to do it, and thousands of devs out there manage to live like that. But that’s definitely not my vision. As a developer (and especially in a field as alive as web development), I believe you need to have certain “qualities” or pre-requisites in order to do this job. Or at least do it well.</p>
<p>One might think that these pre-requisites lie in the “functional” aspect of the human mind. Being good at math might be a good one for starters. That means that you are able to work with rules, and apply them. Speaking english fluently might be another one. It helps reading documentation or comments, understand tutorials… And of course, being a “techie” is a must, even though most of the time this trait does not serve you the way you might think it should. How often do you hear “So you’re a web guy? Hey could you help me with my printer?”</p>
<p>These of course can help. But I believe the main things that make a good developer lies more in the most inner workings of someone. And these could be summed-up by three traits that beautifully work together.</p>
<p>One of these pre-requisites for me is the love for well-made craftsmanship. Being a developer is much like being a woodworker (another of my interests). Your code editor is you hammer. I guess this is why you so often hear developers call themselves “artisans”. Behind this new cliché lies of course a trendy trademark, but I believe in the deeper meaning of the word: that is someone who loves his work, and has the passion for beautiful and well-made artifacts. Or in this case, code. You constantly need to know how to use your tools, how to master techniques, and how to acquire new ones.</p>
<p>This need to learn and apply techniques often leads to another quality: being creative. You may not know how to do something, but your mind can always find a way. Wether by exploring unknown paths, or finding a hidden one, there’s always more than one answer to a problem. Being creative helps you find solutions, and is often fuelled by the third quality that I believe is essential to any web developer: curiosity.</p>
<p>It is thanks to curiosity that, as a child, everyone learns the basics things of life. What’s good, what’s bad. What hurts, what brings joy. Curiosity leads us to meeting new people, confront ideas, and exchange opinions. It is the cornerstone of social interaction, and the best way to develop your mind. It is important to constantly have an open ear, not only to critiques but also to new things that might grasp your interest.</p>
<p>If it’s your passion, you’ll have the tendency to overlook some of these so-called qualities, as they’re simply inner parts of the way you are. If, however, you’re the shy kind of person, my advice to you would be to try to interact more. Only good things can come out of a simple “hello”. You might encounter some obtuse minds or course, but there’s always something to keep from each and every human being you’ll talk with. What’s even more interesting, is that I believe these three angular points are connected. They depend on each other, forming some kind of a chained loop. They come in a certain order, yet you definitely can feel that they influence each other, and are interconnected.</p>
<p>At the end of the day, these traits combined create an interesting and beautiful flow. Curiosity is the spark that triggers Creativity which sublimes itself thanks to good Craftsmanship. It is thanks to these three pillars of the human mind that you’ll be able to enhance and sharpen your skills, and in the end produce more beautiful and efficient code.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>New website!</title>
            <link href="https://www.fabienlasserre.dev/en/blog/new-website/" />
            <updated>2016-01-20T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/new-website/</id>
            <content type="html">
                <![CDATA[
                <p>It’s been a long time coming, but I’ve finally updated this website. Welcome!</p>
<p>With this new website, I’m saying goodbye to Jekyll, and hello Wordpress. I’ve always been a fan of Jekyll, and I still use it today for some of my projects. But lately I’ve been toying around with Wordpress and it’s come a long way over the past few years. It’s now a nice ecosystem to work with, and it will allow me to update this blog faster than before. Fortunately this also means I’ll get to spend more time writing about my projects and how I work.</p>
<p>Unfortunately, most of my work comes with an NDA which prevents me from publicly disclose the projects I’m working on. However, thanks to the folks at <a href="http://www.quasardb.net/">quasardb</a>, I was able to put together a simple “case study” which describes how I put together an app which is used almost everyday in the company. It’s always nice to work with passionate people on an interesting project, and while this particular one was not the biggest I’ve worked on, it was a really rewarding experience.</p>
<p>Oh, and if you’re wondering, yes I’m available to work on your project, so be sure to give me a call or drop me an email so we can discuss about it!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Simply Verify File Hashes With HashCheck</title>
            <link href="https://www.fabienlasserre.dev/en/blog/simply-verify-file-hashes-with-hashcheck/" />
            <updated>2014-07-21T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/simply-verify-file-hashes-with-hashcheck/</id>
            <content type="html">
                <![CDATA[
                <p>Whenever you download a file on the internet, it's a good habit to check its hash <a href="http://en.wikipedia.org/wiki/Checksum">checksum</a> value, especially if that file is an executable. It allows you to verify the integrity of the data you've downloaded and check if it hasn't been altered.</p>
<p><img src="/img/blog/2014-07-21/hashcheck.png" alt="HashCheck"></p>
<p>One very simple way to do this is to use the <a href="http://code.kliu.org/hashcheck/">HashCheck</a> Shell Extension, which will add a tab inside the &quot;Properties&quot; window of any file. It will automatically calculate the checksum of the selected file, and if you happen to have an MD5 or SHA1 value stored in your clipboard, it will automatically compare that value against the calculated checksum of the file you're looking at and tell you if that file's hash is correct. HashCheck is also capable of generating an SFV/MD5/SHA1 file if you need to distribute a file yourself.</p>
<p>HashCheck is free, Open-Source, and available in 20 languages.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>elementary OS, a real alternative to Windows?</title>
            <link href="https://www.fabienlasserre.dev/en/blog/elementary-os-a-real-alternative-to-windows/" />
            <updated>2014-07-14T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/elementary-os-a-real-alternative-to-windows/</id>
            <content type="html">
                <![CDATA[
                <p>For as long as I can remember, I've always wanted to get out of the Windows environment. Windows XP and 7 are outstanding operating systems, but I simply can't get used to Windows 8's user interface. There's also a major drawback when you're doing web development: pretty much everything is done on Unix/Linux systems. Not to mention MacOS X. It has grown so popular over the past few years that it's become cumbersome to try and set up a web development platform on Windows. Try and set up Git or Ruby/Jekyll on a Windows machine and you'll know what I mean. Of course, there are always alternatives (<a href="http://sourcetreeapp.com">Sourcetree</a> for Git is an outstanding product, for example), but there's always something that feels a bit &quot;off&quot; when you're coding on Windows, at least for me. Lately, I've had troubles with Composer while trying to start working again with Laravel.</p>
<h3>Looking For Greener Pastures</h3>
<p>So like every few months, I try to find a new way out by installing a Linux distribution inside a virtual machine. And like most, I've considered bitting the bullet and getting a Mac. But let's be honest, a Macintosh is nothing but an over-hyped, over-priced PC. Plus, the Apple ecosystem feels like a golden prison (and I should know about that, I'm a Google user). Several friends of mine are using Macs, and they couldn't be happier, but I feel like they've signed a pact with the devil whenever I try to use their hardware. It usually ends up like it always does when you're using someone's Apple computer: they spend half an hour explaining how amazing their machine is and &quot;yeah sure it's completely overpriced, I mean I paid $1,299 for mine, but it's totally worth it!&quot;. It's kinda like someone who's decided to inject himself with a cocktail of Smallpox and Ebola in order to lose weight. Sure it's effective, but don't try to convince me it's &quot;worth it&quot; while I'm checking if you've still got a pulse.</p>
<p>Of course, there's always the Hackintosh approach: making a mac using traditional PC parts. But even though there has been a lot of progress in this field, I'm not 14 anymore and I simply don't want to waste time maintaining a computer. I guess that's also why Macs are so popular.</p>
<p>And there's Ubuntu. Ah, Ubuntu. This Operating System feels like that cute girl from high school I never went out with. We flirted, but never got time to hang out together. We stayed in touch over the years, but she could never get passed my weird geeky habits, and despite the fact I've always found her cute, it never happened. Wow, that was an awkward analogy. Anyways, I've known about Ubuntu since around maybe 2005, back when you could simply order a loaded truck of Live CDs to pass around town for a dime. I remember back in the days, when I used to work in retail, I ordered a carton of 200 discs, and gave them to any client that was interested in trying out something new. But even though I've always loved the philosophy behind Ubuntu, I've never made the switch. There <em>always</em> was something to hold me back. Most of the time, it was a hardware incompatibility, other times there was this Windows application I simply couldn't do without. Today, it's Unity. It's something you either love or hate, and I hate it with a passion.</p>
<h3>The Photoshop Case</h3>
<p>If there's something I'm using almost every day but have cold sweats whenever I launch its executable, that's Photoshop. To a designer, it's probably all fun and giggles, but to me it feels like having to deal with a steam engine powered tool while I'm working on this beautiful new <a href="http://www.teslamotors.com/">Tesla</a>. Photoshop is that ugly, awful piece of software everyone around me despise. It's slow, ugly as hell, feels clunky and is basically run by a company that simply doesn't seem to know a single thing about computer security.</p>
<p>Luckily, Gimp is starting to become a real alternative to Photoshop now. A few months ago it revamped its UI to support a desperately need single window mode, and with the ability to import/export PSD files, I'm really starting to consider making the switch completely. Finally ditch Windows, and become... a Linux user. ∗<em>Rolling thunder</em>∗</p>
<h3>elementary OS</h3>
<p><img src="/img/blog/2014-07-14/elementary.png" alt="elementaryos"></p>
<p>All this writing to talk about this linux distribution I've discovered a few days ago, called <a href="http://elementaryos.org/">elementary OS</a>. At its core lies Ubuntu, or to be more precise, the Long Term Support version of Ubuntu. The current version of elementary OS is called Luna, and is base off Ubuntu 12.04. The next version, codenamed Freya, should be available in the upcoming months and will be based off Ubuntu 14.04. Since Ubuntu lies at its core, it's compatible with its repositories and packages. The great difference is the constant desire to bring a beautiful and consistent user interface. At first glance, you can see some elements are heavily based on OSX design, especially with that dock at the bottom of the screen, and that simple bar at the top. And don't call it a global menu, because it's not, and they want you to know that. All the options of an application are available thanks to a simple &quot;gear&quot; menu, right inside the app, and the UI makes use of simple modal windows like OSX does. It features several apps that are custom tailored to fit the design and philosophy behind that distribution, like Geary Mail, a custom Calendar (which you unfortunately can't link to your Google Account), or Shotwell (a photos library). All in all, it feels complete, robust, and slick.</p>
<p><img src="/img/blog/2014-07-14/luna.png" alt="elementary OS Luna"></p>
<p>I'm currently writing this article on elementary OS, and the general feeling of the distribution is absolutely wonderful. Even inside a virtual machine, the thing flies. Granted, I'm using it on a Core i5 processor with 16 GB of RAM, but I'll be honest, I've spent the past five days booting inside windows just to launch that virtual machine. In the span of a few minutes, I was able to install and configure all my productivity apps: Sublime Text, a LAMP server, Git, DBeaver, Chromium, Skype, Spotify... Everything works smoothly and without hiccups.</p>
<p>The only drawback I could find with this distribution is that (very) slow update process. While relying on a LTS release is the assurance that the system will be as stable as possible, if you don't upgrade apps yourself you could end up with an outdated system in a while. I reckon that following Ubuntu's schedule and having an update every six months would be a bit much, and to most users it's not that important to get the latest version of every piece of software they're using, but I'd have loved to get a better update system, or maybe something along the lines of Windows Updates.</p>
<p>Then again, I usually get notified of software updates about the applications I'm using, so I'd be able to upgrade them on my own. And thanks to Ubuntu's Software Center, installing/updating a software has become really simple.</p>
<h3>Minimize it!</h3>
<p>Of course, Luna is not perfect. The first thing you might want to look into is the possibility to add a minimize button on your windows' titles. By default, the only buttons available are Close and Maximize, which are located to the left and right of the windows' title. In order to minimize a window, you need to click its icon on the dock. It makes the layout of windows cleaner, but it's simply not practical.</p>
<p><img src="/img/blog/2014-07-14/luna-buttons.jpg" alt="luna-buttons"></p>
<p>To remedy this problem, simply install <a href="https://apps.ubuntu.com/cat/applications/precise/dconf-tools/">Dconf-tools</a> using Software Center, and find the following key:</p>
<p><strong>org → pantheon → desktop → gala → appearance</strong></p>
<p>Change the value <code>close:maximize</code> to <code>:minimize,maximize,close</code>. This will put all the necessary commands on the far right of the windows' title bar, and Windows user will certainly feel a bit more at home. You'll find a step by step guide over at <a href="http://www.unixmen.com/enable-minimize-move-windows-buttons-elementary-os-luna/">Unixmen</a> that'll show you how to do this.</p>
<h3>Final words</h3>
<p>All in all, I couldn't be happier with that distribution so far. I'll probably give it a solid month of testing, especially with Gimp, before making the final decision. And I'll probably keep a copy of Windows somewhere, because besides work, I'm also a gamer, and even though some huge progress has been made in that field, I'll probably still need it to feed my gaming habits :)</p>
<p>I can totally see elementary OS as my daily driver. For the past week, it hasn't showed the slightest flaw, and I've been productive pretty fast using this new OS. If you wish to give it a try, simply go to <a href="http://elementaryos.org/">http://elementaryos.org/</a>, download the ISO, and cram it inside a <a href="https://my.vmware.com/web/vmware/free#desktop_end_user_computing/vmware_player/6_0">VMWare Player</a> virtual machine. I bet you'll be hooked.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Quasardb has launched</title>
            <link href="https://www.fabienlasserre.dev/en/blog/quasardb-has-launched/" />
            <updated>2013-05-06T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/quasardb-has-launched/</id>
            <content type="html">
                <![CDATA[
                <p>It's not every day that I can talk about my work. Most of the code I'm writing is protected by NDAs, so I rarely have the opportunity to show off what I do.</p>
<p>Today, we successfully launched the new website for <a href="http://www.quasardb.net/">quasardb</a>. Quasardb is an associative database system created by <a href="http://www.bureau14.fr">Bureau 14</a>, which is self-aware, ultra-scallable, and designed from the ground up to operate on huge data volumes at unprecedent speed. The design of the site was done by the folks at <a href="http://www.glamrock-agency.com">Glamrock Agency</a>. I was in charge of taking care of the original integration that was done in XHTML, straighten it a bit by converting it to HTML5, and integrating it back into the Jekyll framework. We've also worked concurrently to optimize the site and the server to the fullest, and it has now a PageSpeed score of 99 out of 100.</p>
<p>It's the second time I've had the pleasure to work with Bureau 14. It's always exciting to work with a company that is so passionate about their product, and technology in general, as the requirements I had to meet were higher than usual. It was a real motivation, as the website had to be perfect, and I'm proud of the work I've put into this project.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Quick Review of Steed FTP Client</title>
            <link href="https://www.fabienlasserre.dev/en/blog/quick-review-of-steed-ftp-client/" />
            <updated>2013-01-14T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/quick-review-of-steed-ftp-client/</id>
            <content type="html">
                <![CDATA[
                <p><img src="/img/blog/2013-01-14/cover.webp" alt="Steed"></p>
<p>As a web developer, much time is spent using an FTP client. And I'm sure you'll agree with me, not so much has been done in the past 15 years in this field. Wether used to backup, upload, or update a website, this piece of code has been used for decades to exchange files between computers. Such a simple protocol shouldn't be reserved to power users, and even some of the nerdiest of us like to use beautiful softwares. Today we're looking at <a href="http://www.frenchfrysoftware.com/steed/">Steed</a>, which got out of Beta in late 2012. Let's see if we can do better than your daddy's client and use 2013's standards with a protocol that's about to celebrate its 40th birthday.</p>
<p>I've been using <a href="http://filezilla-project.org/">FileZilla</a> for as long as I can remember, and despite the fact that it's not very user friendly, it gets the job done. One <em>major</em> drawback though, is the fact it stores its bookmarks' passwords in plain text. It has been <a href="http://trac.filezilla-project.org/ticket/5394">filed as a critical bug</a> in FileZilla's Trac, then rejected on the fact that passwords stored in plain text are not a security failure, but a design choice. This problem can be fixed with ease, using an encrypted <a href="http://www.truecrypt.org/">TrueCrypt container</a> storing the XML file for instance.. I personally use this method and store it in my <a href="http://www.wuala.com/">Wuala</a> folder, which also adds sync between my different machines. Still, this is not a good design decision coming from FileZilla's devs and time has come for a replacement.</p>
<p>After considering the obvious alternatives, and especially <a href="http://winscp.net/eng/index.php">WinSCP</a> (which stores passwords in AES256 <a href="http://winscp.net/eng/docs/security_credentials#storing_password">when using a Master Password</a>), I stumbled upon <a href="http://www.frenchfrysoftware">Steed</a>.com/steed/, a relatively new FTP client from the french folks at <a href="http://www.frenchfrysoftware.com/">French Fry</a>.</p>
<p>After a quick install, an immediate sense of purity and clean design comes to mind. One might even say Apple-esque design, which is a good thing. Steed immediately asked me if I wanted to import my FileZilla bookmarks, and I was ready to go in minutes. Well, not quite, as there was a problem importing my folder settings. I like to link a remote folder with a local one on my computer, and Steed seemed to have some troubles importing some settings regarding folders which are located on a local NAS server. But let's not stop at that.</p>
<div class="is-blog-img"><img src="/img/blog/2013-01-14/steed-main-window.jpg" alt="Steed main window"><p class="is-blog-img-title">Steed main window</p></div>
<p>The main window of Steed shows a Quick Connect section on top, and bookmarks at the bottom of the window. Bookmarks are stored in folders, and are displayed using big squares (around 200px big) showing the type of connection they are using. This is not your typical FTP client, with a list of names spurting out of a dropdown menu, and kudos to French Fry for trying something new. This design however could be improved, as you can't apparently choose a custom picture to replace these big squares, and you can't list your bookmarks in a different, more &quot;traditional&quot; fashion. All in all, it makes this section cluttered and impractical, especially if you have dozens of bookmarks. Apart from that, the UI is as clean as it can get. And it shows in the next screen.</p>
<div class="is-blog-img"><img src="/img/blog/2013-01-14/steed-connected.png" alt="Connected to a S3 bucket"><p class="is-blog-img-title">Connected to a S3 bucket</p></div>
<p>Once connected, you're presented with this view. On the left side, your local machine. On the right side, your distant server. Things couldn't be simplier. Steed is here to transfer files from one location to another, and it gets the job done. Now don't start looking for options like FXP transfers, or even scripting, you'll need something else for that. Steed can handle multiple connections to various servers using tabs, just like Google Chrome or Mozilla Firefox. Unfortunately, these tabs don't seem to regroup in the taskbar, and I was shown multiple icons as if multiple instances of Steed were running. Another thing that could be improved with this simplistic UI is the fact that you have to open a new tab in order to access the Quick Connect / Bookmarks page again. It seems to be a design choice, but having a direct access to bookmarks (for instance thanks to an icon next to the Wrench icon) could save some clicks and add a feature while keeping in line with the minimalistic approach.</p>
<p>One other thing I absolutely love is the fact Steed handles not only FTP and SFTP, but also Amazon S3 buckets. It's something WinSCP can't handle, and <a href="http://winscp.net/forum/viewtopic.php?t=6808">it has been confirmed by its developper</a> that he simply does not want to implement this feature. With Steed, using an S3 bucket couldn't be simplier. Just specify an access key and a secret key and your buckets will appear just like any other FTP folder. Now if the folks at French Fry are reading this, it would be awesome if Steed could also act as a client for Amazon Glacier, as there is no real good software out there for this purpose (<em>*wink wink*</em>).</p>
<p>OK so we're connected, now let's use this puppy.</p>
<div class="is-blog-img"><img src="/img/blog/2013-01-14/steed-upload-s3.png" alt="Transfering some files"><p class="is-blog-img-title">Transfering some files</p></div>
<p>When uploading a file, a nifty progress bar appears at the bottom of the window. Steed is supposed to be seamlessly integrated with Windows 7, so you should see the progress bar inside the shortcut icon of the application, but unfortunately I wasn't able to get a correct result for the tests I did (both using FTP or S3), and the icon showed some bugs regarding the progress bar. A quick click on a button located at the bottom of the main window opens a panel showing the details of the current transfers. Once again, the informations are displayed beautifully, and you can pause/resume/remove files in the queue.</p>
<p>And remember when I told you one of the main gripes I had about FileZilla was security? Well, what about Steed? I asked the devs via Twitter what was their crypto schema, and apparently, the credentials are stored in an xml file (just like FileZilla), but the passwords themselves are encrypted using Rijndael, a salted passphrase, and SHA1 as a hash. Much much better than FileZilla.</p>
<p>So it's all good and everything, but unfortunately for the dudes at French Fry, some of the downsides are huge.</p>
<p>First of all, Steed seems to be quite the ressource hog.</p>
<div class="is-blog-img"><img src="/img/blog/2013-01-14/steed-memory.png" alt="Steed memory usage vs. Filezilla"><p class="is-blog-img-title">Steed memory usage vs. Filezilla</p></div>
<p>On my Asus Ultrabook, after one hour of &quot;stand-by&quot; use (the application was just connected to an FTP server, and ran in the background), Steed gradually took nearly 140 MB of RAM, while FileZilla was stuck at a mere 8 MB.</p>
<p>Then comes the price. While I'm totally for buying software and supporting developpers (I'm writing this article from my precious Sublime Text 2), Steed comes at a steep price of 19,99 € (around $26). I personally think in this Freemium Softwares, Smartphone Apps and Digital Distribution time and age, such a simple software could sell much better at around 9,99 € (with, say, an introduction price of 7,99€). Now I don't have the exact sell figures, and I hope things are doing well for French Fry, but as a comparison I bought a licence of <a href="http://www.postbox-inc.com/">PostBox</a> (an amazing Email client based on Mozilla Thunderbird) last month for $9.95, which seemed like a bargain to me. At 20 Euros, and considering all the downsides I've seen, I came to the conclusion that Steed was not worth it and in the end I didn't buy a licence.</p>
<h3>Conclusion</h3>
<p>Now I don't mean to tell everyone they should avoid this software. On the contrary, this new FTP client feels like a dose of fresh air in a field were editors haven't really tried anything new for a few decades. But despite all the good things Steed wants to offer, it lacks some polishing. And it's a shame considering its main selling point is its &quot;simple &amp; beautiful&quot; UI. Some obvious features (a better sorting system for the bookmarks, the ability to clear the process queue...) are lacking, and with a steep price tag, Steed confronts itself with alternatives which are certainely not as attractive, but are free and (for the most part) flawless. I hope the folks at French Fry will nail the things that prevented me (and probably others) from buying a licence, because this new FTP client has a tremendous potential. And if you're looking for a simple, beautiful FTP client, you definitely need to check it out, as you get 10 days of free use when installing the client for the first time.</p>
<p><em><em>Update (March 20th, 2013):</em></em><br>
Following this article, French Fry contacted me and asked for my inputs on what went wrong during my testing phase. Even though this article wasn't that easy on them, they were open to criticism and I'm happy to report that they're already working on fixing some of the bugs, like the one I had with the FileZilla folders import. Same for the progress bar bug, a fix will be deployed with the next version. They'll also soon add an option to change the language of the application. Much work is being done regarding memory optimization, and French Fry suspects the .NET framework to be responsible for much of the heavy memory usage of Steed. I ran more tests on my end, and Steed felt more stable than when I first reviewed it. I was able to pinpoint a memory error related to Amazon S3, which apparently makes the software gobble RAM like it's candy when uploading a file. Apart from that usage, Steed was much more stable, staggering at around 70 MB (still huge considering it's an FTP client, but I'm glad to see my first assumption was wrong). We also exchanged on the possibility of Glacier support (I hope they'll implement this soon), and a better bookmark system. They agree something has to be done, but they're looking for the best way to implement this inside the application without breaking the usage flow.</p>
<p>So their software is on its way to become an even better client than it already is, and kudos to French Fry for being that open. Steed is a software I'll definitely keep an eye on.</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>The Rubber Duck Debugging</title>
            <link href="https://www.fabienlasserre.dev/en/blog/the-rubber-duck-debugging/" />
            <updated>2012-11-18T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/the-rubber-duck-debugging/</id>
            <content type="html">
                <![CDATA[
                <p>Following the post by my good friend <a href="http://www.marcomonteiro.net/">Marco Monteiro</a> about the <a href="http://blog.marcomonteiro.net/post/35697947390/yoda-conditions/">Yoda Condition</a>, I thought it'd be neat to share one of my favorite way of maintening code: The Rubber Duck Debugging. Or to be a bit more precise, my own approach on the concept. Let me explain.</p>
<p>Also called &quot;<strong>Rubber Ducking</strong>&quot;, or even &quot;<strong>Confessional Debugging</strong>&quot;, the Rubber Duck Debugging is a method of, well, debugging code. It consists of forcing yourself to explain, line-by-line, your code to... a rubber duck. The goal here is to be able to coin errors or bugs by yourself, speaking to an inanimate object. In describing what the code is supposed to do and observing what it actually does, any inconsistent behavior should rise by itself.</p>
<p>Now, I don't keep a rubber ducky on my desk at all time (I have a little alien toy with four eyes) and to be honest I don't do Rubber Ducking, ever. But what I particularly like about this approach is the fact that good written code should be self-explainatory. In a way, one should produce code so clear even a rubber duck could understand it. Another way of putting it is this quote I've always loved:</p>
<p>{.quote}Always code as if the person who will maintain your code is a maniac serial killer that knows where you live.</p>
<p>Because let's face it, when having to take the relay on a project, we absolutely despise the person who coded the stuff we have to work on. It's always badly fragmented, poorly written, has too many comments (or absolutely none). That's why you should always be aware that even though you're the only one working on a project at a given time, you never know who's going to maintain or modify it. Even worse, you could be working on a project and let it sleep for months before having to work on it again. And you'll hate yourself having to spend more time than necesary in trying to understand what you wrote earlier that year.</p>
<p><em>Beautiful code is hard to produce.</em> It's always easier to quick patch something, and forget about adding a comment line. But that single error could lead to a dreadful <a href="http://blogea.bureau14.fr/index.php/2012/09/the-butterfly-bug/">Butterfly Bug</a> and make you lose precious hours trying to fix something that could've been prevented easely. That's why it's always a good idea to take your time, plan ahead, and even doodle your code (another habit of mine, I'll probably explain that in another post). That's also why it's always a good idea to keep a four-eyed alien on your desk. He's always watching!</p>

                ]]>
            </content>
        </entry>
    
        
        <entry>
            <title>Blog</title>
            <link href="https://www.fabienlasserre.dev/en/blog/" />
            <updated>1970-01-01T00:00:00Z</updated>
            <id>https://www.fabienlasserre.dev/en/blog/</id>
            <content type="html">
                <![CDATA[
                
                ]]>
            </content>
        </entry>
    
</feed>
