<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Vincent Prouillet: Developer &amp; Entrepreneur</title>
      <link>https://www.vincentprouillet.com</link>
      <description>Vincent Prouillet blog</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.vincentprouillet.com/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Fri, 04 Sep 2020 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Releasing Zola 0.12</title>
          <pubDate>Fri, 04 Sep 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-zola-0-12/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-zola-0-12/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-zola-0-12/">&lt;p&gt;A brand new &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; version is out! Zola is a flexible static site engine.&lt;&#x2F;p&gt;
&lt;p&gt;This new version doesn&#x27;t contain a lot of new changes, summer and global pandemic not being the most productive
environment - at least for me.&lt;&#x2F;p&gt;
&lt;p&gt;The main improvement of this release is completely changing how &lt;code&gt;zola serve&lt;&#x2F;code&gt;. Before 0.12, it used to write the whole site
to disk and on changes try to figure out what changed and the minimum things we need to rebuild. It sounds good
in theory but in practice was very buggy as Zola is pretty flexible: you can access the site content from any template
(for example there could be a box with the most recent 5 articles on every page). And, well, it was trying to be too smart.&lt;&#x2F;p&gt;
&lt;p&gt;With 0.12, the rendered content will be stored in a HashMap in memory instead. Only the HTML content is stored in that
HashMap, the assets stay on disk. When anything changes in a template or in a Markdown file, we rebuild &lt;em&gt;everything&lt;&#x2F;em&gt;.
This is slower than the previous mechanism but has the advantage of always producing the correct output.
The downside is that it is slower: making a change to a Markdown file on the Zola documentation used to do a rebuild
in 8ms and would now take 200ms. Still fast enough for the majority of cases. If you have a huge site and the rebuild
is now too slow, you can use &lt;code&gt;zola serve --fast&lt;&#x2F;code&gt; that rebuilds only the section or page that changed and gets you back to
the 8ms rebuild time.
Hopefully this approach works better for most people!&lt;&#x2F;p&gt;
&lt;p&gt;There are a few other changes that you can see in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#0120-2020-09-04&quot;&gt;CHANGELOG&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The next version will be focused on finishing the multi-language story with a full i18n integration based on
&lt;a href=&quot;https:&#x2F;&#x2F;www.projectfluent.org&#x2F;&quot;&gt;Fluent&lt;&#x2F;a&gt; as well as adding some options to he Markdown rendering, such as smart
punctuation.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using CSS modules with Sass, Webpack and TypeScript</title>
          <pubDate>Fri, 24 Jan 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/using-sass-modules-typescript/</link>
          <guid>https://www.vincentprouillet.com/blog/using-sass-modules-typescript/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/using-sass-modules-typescript/">&lt;p&gt;A few months ago, I mentioned in my articles about setting up Webpack with TypeScript (&lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;react-typescript-webpack-1&#x2F;&quot;&gt;part 1&lt;&#x2F;a&gt; and
&lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;react-typescript-webpack-2&#x2F;&quot;&gt;part 2&lt;&#x2F;a&gt;) that CSS modules would be a nice addition to it. Here we go!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-css-modules&quot;&gt;What are CSS modules?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-are-css-modules&quot; aria-label=&quot;Anchor link for: what-are-css-modules&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One common issue for anyone writing CSS is &lt;strong&gt;specificity&lt;&#x2F;strong&gt;. As a site grows, the number of CSS rules and classes grow.
This makes it easy to accidentally re-use an existing class name by inadvertence and wonder why your text is now red.&lt;&#x2F;p&gt;
&lt;p&gt;CSS modules are pretty simple. Let&#x27;s start by writing CSS&#x2F;Sass&#x2F;Less as you would normally do:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-css &quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.my-class &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;color&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#da5d42;&quot;&gt;red&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.title &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;font-size&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ca7840;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can then import the file in your &lt;code&gt;.js&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;.ts&lt;&#x2F;code&gt; variable (like &lt;code&gt;import styles from &quot;_style.scss&quot;;&lt;&#x2F;code&gt;) and access the class names from it: &lt;code&gt;styles.title&lt;&#x2F;code&gt;.
Not very impressive but if you look at the source of your HTML page you will notice the class is some random combination of letters and numbers.
This is the powerful part of CSS modules: each class gets its own unique name which means that there cannot be specificity issues anymore.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few other ways to solve the specificity issues:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;use something like &lt;a href=&quot;http:&#x2F;&#x2F;getbem.com&#x2F;&quot;&gt;BEM&lt;&#x2F;a&gt;, which requires discipline&lt;&#x2F;li&gt;
&lt;li&gt;use a CSS-in-JS library like &lt;a href=&quot;https:&#x2F;&#x2F;emotion.sh&#x2F;docs&#x2F;introduction&quot;&gt;emotion&lt;&#x2F;a&gt;, which is more complex and has lower performance than alternatives&lt;&#x2F;li&gt;
&lt;li&gt;hope for the best&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;adding-css-module-to-the-webpack-boilerplate&quot;&gt;Adding CSS module to the Webpack boilerplate&lt;a class=&quot;zola-anchor&quot; href=&quot;#adding-css-module-to-the-webpack-boilerplate&quot; aria-label=&quot;Anchor link for: adding-css-module-to-the-webpack-boilerplate&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Turns out enabling them is really easy! The only tricky part is, as some might have guessed from the example above, ensuring
TypeScript does not error when loading the Sass file. Thankfully, a loader exists to automatically create definition file from CSS files:
&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;@teamsupercell&#x2F;typings-for-css-modules-loader&quot;&gt;typings-for-css-modules-loader&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s go quickly through the main changes needed to the Webpack configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-          &#x2F;&#x2F; Translates CSS into CommonJS
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-          &amp;quot;css-loader&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+          {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            loader: &amp;quot;@teamsupercell&#x2F;typings-for-css-modules-loader&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            options: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+              banner: &amp;quot;&#x2F;&#x2F; autogenerated by typings-for-css-modules-loader.&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            }
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+          },
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+          &#x2F;&#x2F; Translates CSS into CommonJS with modules
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+          {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            loader: &amp;quot;css-loader&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            options: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+              modules: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                mode: &amp;quot;local&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                localIdentName: &amp;#39;[local]--[hash:base64:6]&amp;#39;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+              },
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+              localsConvention: &amp;quot;camelCase&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            }
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+          },
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We go from a plain &lt;code&gt;css-loader&lt;&#x2F;code&gt; to one with modules enabled with a &lt;code&gt;local&lt;&#x2F;code&gt; scope and where the generated classes are still readable.
We also tell the loader to camel case the classes in order to satisfy the various linters.
Lastly, we set the &lt;code&gt;@teamsupercell&#x2F;typings-for-css-modules-loader&lt;&#x2F;code&gt; &lt;strong&gt;above&lt;&#x2F;strong&gt; the &lt;code&gt;css-loader&lt;&#x2F;code&gt;, the position is important.
There are other changes in the commit, such as ignoring all generated files, that are pretty straightforward.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;9786130bf283d47af1d1945ac66679a0529254c4&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;9786130bf283d47af1d1945ac66679a0529254c4&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;anything-missing&quot;&gt;Anything missing?&lt;a class=&quot;zola-anchor&quot; href=&quot;#anything-missing&quot; aria-label=&quot;Anchor link for: anything-missing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have CSS modules in place, the only thing I consider missing is &lt;a href=&quot;https:&#x2F;&#x2F;storybook.js.org&#x2F;&quot;&gt;Storybook&lt;&#x2F;a&gt;. This is
unlikely to get an article as it is pretty easy to follow the instructions to install it though.&lt;&#x2F;p&gt;
&lt;p&gt;Are there any amazing libraries&#x2F;Webpack plugins I&#x27;m missing out on?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tera v1 is here!</title>
          <pubDate>Sat, 07 Dec 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/tera-v1-is-here/</link>
          <guid>https://www.vincentprouillet.com/blog/tera-v1-is-here/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/tera-v1-is-here/">&lt;p&gt;Tera is a template engine inspired by &lt;a href=&quot;http:&#x2F;&#x2F;jinja.pocoo.org&#x2F;&quot;&gt;Jinja2&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;1.9&#x2F;topics&#x2F;templates&#x2F;&quot;&gt;Django template language&lt;&#x2F;a&gt;.
It is also similar to &lt;a href=&quot;https:&#x2F;&#x2F;twig.symfony.com&#x2F;&quot;&gt;Twig&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;shopify.github.io&#x2F;liquid&#x2F;&quot;&gt;Liquid&lt;&#x2F;a&gt; if you are coming from PHP&#x2F;Ruby.&lt;&#x2F;p&gt;
&lt;p&gt;The last release, 0.11.20, was released back in November 2018 and the first v1 alpha was released in January 2019. During 2019,
the &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; static engine has been keeping up with each Tera release and has therefore been tested quite a bit.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to see the TL;DR of all the changes since 0.11.20, you can have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#100-2019-12-07&quot;&gt;changelog&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In most cases, the upgrade to v1 should be painless: unless you are using the error-chain part of Tera errors, it might
even work without any changes.&lt;&#x2F;p&gt;
&lt;p&gt;Before going further, a huge thank you to everyone that &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;graphs&#x2F;contributors&quot;&gt;contributed&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s look at some of the main changes in a bit more detail now.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;error-handling&quot;&gt;Error handling&lt;a class=&quot;zola-anchor&quot; href=&quot;#error-handling&quot; aria-label=&quot;Anchor link for: error-handling&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Error handling is a &lt;a href=&quot;https:&#x2F;&#x2F;blog.yoshuawuyts.com&#x2F;error-handling-survey&#x2F;&quot;&gt;hot topic&lt;&#x2F;a&gt; in Rust and has been for quite some time.
At the time of 0.11, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang-nursery&#x2F;error-chain&quot;&gt;error-chain&lt;&#x2F;a&gt; was the recommended approach but pretty much as
soon as I ported all of my code to use it, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang-nursery&#x2F;failure&#x2F;&quot;&gt;failure&lt;&#x2F;a&gt; became the go-to crate. Since then,
some of &lt;code&gt;failure&lt;&#x2F;code&gt;&#x27;s features made their way into the standard &lt;code&gt;Error&lt;&#x2F;code&gt; trait including the one I am using: the &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;error&#x2F;trait.Error.html#method.source&quot;&gt;&lt;code&gt;source&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; method.&lt;&#x2F;p&gt;
&lt;p&gt;I have pretty much given up on any error handling crate for the time being and just use &lt;code&gt;std::Error&lt;&#x2F;code&gt; for everything, including in this new version of Tera.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;traits-for-filter-tests-and-functions&quot;&gt;Traits for filter, tests and functions&lt;a class=&quot;zola-anchor&quot; href=&quot;#traits-for-filter-tests-and-functions&quot; aria-label=&quot;Anchor link for: traits-for-filter-tests-and-functions&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Previously, the type for each was a simple function. It has now been changed to a trait, allowing for each of them to have a context, for example.
Zola, for example, uses structs for some of the functions in order to hold the site data. The trait is automatically implemented for the previous function
type so if you had defined your own they should continue to work, as soon as you change their arguments to be borrowed instead of owned.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;whitespace-management&quot;&gt;Whitespace management&lt;a class=&quot;zola-anchor&quot; href=&quot;#whitespace-management&quot; aria-label=&quot;Anchor link for: whitespace-management&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can now use the same whitespace management as Jinja2 in Tera:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;jinja2&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-jinja2 &quot;&gt;&lt;code class=&quot;language-jinja2&quot; data-lang=&quot;jinja2&quot;&gt;&lt;span&gt;hello
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{{- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;username &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;-}}
&lt;&#x2F;span&gt;&lt;span&gt;!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;will render &lt;code&gt;hellovincent!&lt;&#x2F;code&gt; if &lt;code&gt;username&lt;&#x2F;code&gt; is &lt;code&gt;vincent&lt;&#x2F;code&gt;. Please see the &lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;docs&#x2F;#whitespace-control&quot;&gt;documentation&lt;&#x2F;a&gt; for more details.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;increased-rendering-performance&quot;&gt;Increased rendering performance&lt;a class=&quot;zola-anchor&quot; href=&quot;#increased-rendering-performance&quot; aria-label=&quot;Anchor link for: increased-rendering-performance&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The whole rendering code has been rewritten to be more performant.&lt;&#x2F;p&gt;
&lt;p&gt;The bottleneck is still converting the context to JSON since it needs to clone the data.
This is unlikely to change until we move to a borrowed approach but I do not know how to approach that. If
anyone can figure out a way to solve this issue and still be ergonomic, that would be amazing; please chime in on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;469&quot;&gt;related issue&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;How does it compare with other Rust template engines? There is a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;djc&#x2F;template-benchmarks-rs&quot;&gt;benchmark&lt;&#x2F;a&gt; testing just that.
Before showing the results, it is important to understand the difference between the compiled templates and interpreted templates:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;compiled template engines: &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;askama&quot;&gt;askama&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;horrorshow&quot;&gt;horrorshow&lt;&#x2F;a&gt; and most of the ones
in that benchmark are generating Rust code through macros. The big upsides of this are performance and being able to typecheck your templates.
The big downsides are compilation time and not being able to handle dynamic templates: you cannot render an arbitrary template file.&lt;&#x2F;li&gt;
&lt;li&gt;interpreted template engines: &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;liquid&quot;&gt;Liquid&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;handlebars&quot;&gt;Handlebars&lt;&#x2F;a&gt; and Tera are in this category.
They are not going to be as fast or type-safe as compiled one but can run any templates you throw at them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For example, a static site engine could not work using a compiled template engine as users define their own templates, add arbitrary data and extend other random existing templates.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve grouped the table output of the templates benchmark (done with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bheisler&#x2F;criterion.rs&quot;&gt;criterion.rs&lt;&#x2F;a&gt;) depending on the template engine type.
The benchmarks have been ran on a 2018 Macbook Pro.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;big-table-benchmark&quot;&gt;Big table benchmark&lt;a class=&quot;zola-anchor&quot; href=&quot;#big-table-benchmark&quot; aria-label=&quot;Anchor link for: big-table-benchmark&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is running a for loop with a context being an array of length 100 with each element being another array of length 100.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Askama        time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;624.98 us 643.77 us 663.40 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;fomat         time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;228.47 us 230.94 us 234.08 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Horrorshow    time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;357.87 us 371.52 us 385.84 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]  
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Markup        time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;294.43 us 302.90 us 313.99 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Ructe         time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;586.48 us 609.61 us 638.02 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Yarte         time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;286.75 us 311.03 us 334.20 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;write         time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;353.75 us 370.98 us 391.61 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Tera          time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;3.5811 ms 3.6997 ms 3.8292 ms&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Liquid        time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;12.890 ms 13.113 ms 13.366 ms&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Big&lt;&#x2F;span&gt;&lt;span&gt; table&#x2F;Handlebars    time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;83.598 ms 86.947 ms 90.391 ms&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]   
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, this is a bad case for interpreted template engine. Tera is the fastest among them but is still up to 10x slower than some compiled engines.
The result from Handlebars are particularly odd: I do not see why it would be 23x slower than Tera.&lt;&#x2F;p&gt;
&lt;p&gt;Considering those results, you should probably use a compiled template engine if you are trying to render some context
requiring a lot of allocations like in that benchmark.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;teams-benchmark&quot;&gt;Teams benchmark&lt;a class=&quot;zola-anchor&quot; href=&quot;#teams-benchmark&quot; aria-label=&quot;Anchor link for: teams-benchmark&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is rendering a template with a small array of structs as context, a more realistic average workload.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Askama&lt;&#x2F;span&gt;&lt;span&gt;            time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;1.1503 us 1.2120 us 1.2800 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;fomat&lt;&#x2F;span&gt;&lt;span&gt;             time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;502.20 ns 512.54 ns 524.81 ns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Horrorshow&lt;&#x2F;span&gt;&lt;span&gt;        time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;466.38 ns 469.09 ns 472.31 ns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Markup&lt;&#x2F;span&gt;&lt;span&gt;            time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;403.39 ns 408.75 ns 416.11 ns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Ructe&lt;&#x2F;span&gt;&lt;span&gt;             time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;963.43 ns 976.63 ns 989.98 ns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Yarte&lt;&#x2F;span&gt;&lt;span&gt;             time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;766.24 ns 782.78 ns 800.46 ns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;write&lt;&#x2F;span&gt;&lt;span&gt;             time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;756.18 ns 770.13 ns 784.21 ns&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Tera&lt;&#x2F;span&gt;&lt;span&gt;              time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;8.8657 us 8.8969 us 8.9288 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Liquid&lt;&#x2F;span&gt;&lt;span&gt;            time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;12.858 us 12.909 us 12.960 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;] 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Teams&#x2F;Handlebars&lt;&#x2F;span&gt;&lt;span&gt;        time:   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;70.150 us 75.716 us 81.926 us&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Tera is again the fastest interpreted template engine, but the numbers are small enough that any engine will do the job.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;glob-patterns-now-supported&quot;&gt;Glob patterns now supported&lt;a class=&quot;zola-anchor&quot; href=&quot;#glob-patterns-now-supported&quot; aria-label=&quot;Anchor link for: glob-patterns-now-supported&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can now pass patterns to &lt;code&gt;Tera::new&lt;&#x2F;code&gt; like &lt;code&gt;templates&#x2F;**&#x2F;*{html,xml}&lt;&#x2F;code&gt; to load exactly the type of files you want.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-builtins-feature&quot;&gt;A &lt;code&gt;builtins&lt;&#x2F;code&gt; feature&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-builtins-feature&quot; aria-label=&quot;Anchor link for: a-builtins-feature&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The number of dependencies started to grow quite a bit as filters and functions were being added.
From &lt;code&gt;1.0.0&lt;&#x2F;code&gt;, a new active-by-default feature named &lt;code&gt;builtins&lt;&#x2F;code&gt; has been introduced. If you disable it, Tera will only
depend on the crates needed for parsing and rendering and all the filters and functions that were depending on those
dependencies will not be present anymore.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;known-issue&quot;&gt;Known issue&lt;a class=&quot;zola-anchor&quot; href=&quot;#known-issue&quot; aria-label=&quot;Anchor link for: known-issue&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Due to a bug in &lt;a href=&quot;https:&#x2F;&#x2F;pest.rs&#x2F;&quot;&gt;pest&lt;&#x2F;a&gt;, the parser generator used by Tera, some template parsing might timeout. There
are more details in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;436&quot;&gt;issue&lt;&#x2F;a&gt; but since the pest contributors are working on v3,
it doesn&#x27;t seem worth it to hold on releasing Tera 1.0 for an unknown amount of time until its release.
This issue was found while fuzzing but I&#x27;ve been running into &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;66140&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;66140&lt;&#x2F;a&gt; when trying
to run the fuzzer again on OSX.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting up a React+TypeScript frontend with Webpack — Part 2</title>
          <pubDate>Fri, 01 Nov 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/react-typescript-webpack-2/</link>
          <guid>https://www.vincentprouillet.com/blog/react-typescript-webpack-2/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/react-typescript-webpack-2/">&lt;p&gt;In this article, we are going to turn our &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;react-typescript-webpack-1&#x2F;&quot;&gt;local environment setup&lt;&#x2F;a&gt; into
a production-ready setup. Thanks to improvements in recent Webpack versions, it is now straightforward.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;node-env-production-in-webpack&quot;&gt;NODE_ENV=production in Webpack&lt;a class=&quot;zola-anchor&quot; href=&quot;#node-env-production-in-webpack&quot; aria-label=&quot;Anchor link for: node-env-production-in-webpack&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The majority of JavaScript projects use the &lt;code&gt;NODE_ENV&lt;&#x2F;code&gt; variable to enable optimizations and remove debug code. If you need
to support Windows, setting an environment variable directly in the script the UNIX way is not going to work. You can install &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;cross-env&quot;&gt;cross-env&lt;&#x2F;a&gt; to handle cross-platform environment variables.
The script in that case is &lt;code&gt;&quot;build:prod&quot;: &quot;cross-env NODE_ENV=production webpack&quot;&lt;&#x2F;code&gt;.
Again, if you do not need Windows support, simply remove the &lt;code&gt;cross-env&lt;&#x2F;code&gt; from the script.&lt;&#x2F;p&gt;
&lt;p&gt;Now there are two ways to proceed:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;split the configuration in multiple files and merge them with something like &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;webpack-merge&quot;&gt;webpack-merge&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;keep everything in one file and use JavaScript to toggle things&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I prefer the second approach as I find it more readable and doesn&#x27;t require additional dependencies. You can judge for yourself
by looking at the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;blob&#x2F;master&#x2F;webpack.config.js&quot;&gt;final result&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned before, Webpack has been making things easier. With the introduction of &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;configuration&#x2F;mode&#x2F;&quot;&gt;mode&lt;&#x2F;a&gt;, it is easy
to get a production-ready bundle:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;webpack.config.js b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span&gt;index 53c39f2..b4d6e11 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;--- a&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;+++ b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -4,9 +4,11 @@ &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;color:#c23f31;&quot;&gt;const HtmlWebpackPlugin = require(&amp;quot;html-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; const ForkTsCheckerWebpackPlugin = require(&amp;quot;fork-ts-checker-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; const { CleanWebpackPlugin } = require(&amp;quot;clean-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+const isProd = process.env.NODE_ENV === &amp;quot;production&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+
&lt;&#x2F;span&gt;&lt;span&gt; module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;   context: __dirname,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-  mode: &amp;quot;development&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+  mode: isProd ? &amp;quot;production&amp;quot; : &amp;quot;development&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;   entry: {
&lt;&#x2F;span&gt;&lt;span&gt;     app: &amp;quot;.&#x2F;src&#x2F;index.tsx&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;   },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Just changing the mode will get us a minified output: you can check it by running &lt;code&gt;build:prod&lt;&#x2F;code&gt; yourself.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the minified code, we can see that the sourcemap is embedded in the file still.
The &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;configuration&#x2F;devtool&#x2F;#production&quot;&gt;best option for production environments&lt;&#x2F;a&gt;
is &lt;code&gt;source-map&lt;&#x2F;code&gt; which we can set conditionally on the &lt;code&gt;isProd&lt;&#x2F;code&gt; variable: &lt;code&gt;devtool: isProd ? &quot;source-map&quot; : &quot;eval-source-map&quot;,&lt;&#x2F;code&gt;.
Running &lt;code&gt;build:prod&lt;&#x2F;code&gt; again will now properly create a separate file ending in &lt;code&gt;.js.map&lt;&#x2F;code&gt;.
Remember that the sourcemap files should not be accessible by anyone other than you and your bug reporting app.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, the HMR from Webpack is injecting some code in the bundle that we do not want in our production bundle. We can disable it easily:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;webpack.config.js b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span&gt;index af5fd2f..63272fd 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;--- a&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;+++ b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -58,6 +58,6 @@ &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;color:#c23f31;&quot;&gt;module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;       async: false
&lt;&#x2F;span&gt;&lt;span&gt;     }),
&lt;&#x2F;span&gt;&lt;span&gt;     new CleanWebpackPlugin(),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-    new webpack.HotModuleReplacementPlugin()
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-  ]
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+    isProd ? false : new webpack.HotModuleReplacementPlugin()
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+  ].filter(Boolean)
&lt;&#x2F;span&gt;&lt;span&gt; };
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;4527184f2d7a49447451774b1bf5cc8d4cb3c1e5&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;4527184f2d7a49447451774b1bf5cc8d4cb3c1e5&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;splitting-chunks&quot;&gt;Splitting chunks&lt;a class=&quot;zola-anchor&quot; href=&quot;#splitting-chunks&quot; aria-label=&quot;Anchor link for: splitting-chunks&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In our current situation, we generate a single file for the whole app: it clocks in at 132KB before gzip.
This means that every time someone changes anything in the codebase, the full bundle will be invalidated and re-downloaded by every users.&lt;&#x2F;p&gt;
&lt;p&gt;There are two approaches to splitting the chunks:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;bundle splitting&lt;&#x2F;strong&gt;: separating into fixed bundles, for example putting dependencies into another bundle&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;code splitting&lt;&#x2F;strong&gt;: splitting your own code into multiple bundles loaded on demand&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;bundle-splitting&quot;&gt;Bundle splitting&lt;a class=&quot;zola-anchor&quot; href=&quot;#bundle-splitting&quot; aria-label=&quot;Anchor link for: bundle-splitting&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The easiest bundle splitting strategy is to create a &lt;code&gt;vendors&lt;&#x2F;code&gt; bundle containing all dependencies. In our case we currently
only have &lt;code&gt;react&lt;&#x2F;code&gt; and &lt;code&gt;react-dom&lt;&#x2F;code&gt; but it will inevitably grow and, unless you are upgrading dependencies every day, will not
change very often. Dividing the bundle does not change anything for first-time users as they will have to download all files but
repeat users will only download the bundles that changed.&lt;&#x2F;p&gt;
&lt;p&gt;Webpack comes with a built-in option to split everything coming from the &lt;code&gt;node_modules&lt;&#x2F;code&gt; folder to another bundle: &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;plugins&#x2F;split-chunks-plugin&#x2F;#splitchunkschunks&quot;&gt;optimization.splitChunks.chunks&lt;&#x2F;a&gt;.
Setting it to &lt;code&gt;&quot;all&quot;&lt;&#x2F;code&gt; will produce a new JavaScript file with a name starting by &lt;code&gt;vendors~app&lt;&#x2F;code&gt; in the output directory weighting 131KB while the app bundle shrank down to 1.9KB.
If you run &lt;code&gt;build:prod&lt;&#x2F;code&gt; again after making a change to &lt;code&gt;index.tsx&lt;&#x2F;code&gt;, you will notice the hash did change: we need to ensure the hash
is done on the content rather than the build. In practice this means changing &lt;code&gt;[hash]&lt;&#x2F;code&gt; to &lt;code&gt;[contenthash]&lt;&#x2F;code&gt; in the &lt;code&gt;output.filename&lt;&#x2F;code&gt; configuration in production.
Trying it again will now give the expected results: a change in our application code doesn&#x27;t change the &lt;code&gt;vendors&lt;&#x2F;code&gt; bundle.&lt;&#x2F;p&gt;
&lt;p&gt;For many apps, just splitting the bundle that way will be enough to get started. If needed, you can create different kind
of strategies, for example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;one file per &lt;code&gt;npm&lt;&#x2F;code&gt; dependency&lt;&#x2F;li&gt;
&lt;li&gt;group packages per update of frequency: put the ones that change often in the same bundle&lt;&#x2F;li&gt;
&lt;li&gt;group packages per kind: all React packages could go in one bundle, all data viz in another etc&lt;&#x2F;li&gt;
&lt;li&gt;have multiple entry points: Webpack will create one file per entry point&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;plugins&#x2F;split-chunks-plugin&#x2F;#splitchunkscachegroups&quot;&gt;Webpack documentation&lt;&#x2F;a&gt;
has some examples demonstrating how to implement some of the strategies. Once again, start with the default dumb splitting and experiment as you go when needed.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;cd2669b62d9542794fdc52e6e6fb1702716ddc36&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;cd2669b62d9542794fdc52e6e6fb1702716ddc36&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;code-splitting&quot;&gt;Code splitting&lt;a class=&quot;zola-anchor&quot; href=&quot;#code-splitting&quot; aria-label=&quot;Anchor link for: code-splitting&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Code splitting is allowing you to import some code on demand. It relies on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tc39&#x2F;proposal-dynamic-import&quot;&gt;dynamic import() proposal&lt;&#x2F;a&gt; which
is now on stage 4, eg finished. A simple React example would be:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;React &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;LocationForm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;React&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{}&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;handleClickOnMap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;locationModal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(({ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;locationModal &lt;&#x2F;span&gt;&lt;span&gt;}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Use locationModal
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;catch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Handle failure
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;   }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;         &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;button onClick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;handleClickOnMap&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;}&amp;gt;Select location&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;button&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;       &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;     );
&lt;&#x2F;span&gt;&lt;span&gt;   }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Imagine that the &lt;code&gt;locationModal&lt;&#x2F;code&gt; component is loading &lt;a href=&quot;https:&#x2F;&#x2F;leafletjs.com&#x2F;&quot;&gt;Leaflet&lt;&#x2F;a&gt; and that this is the only place
where it is used. With the bundle splitting, Leaflet would be in your bundle even for users never seeing that form. If you are
doing code splitting, you will obviously need to make sure Leaflet is not part of another chunk. Another
obvious contender for code splitting is data visualisation: plottting libraries are typically heavy and if you only have them in one page you can split it from your bundle to
provide a faster experience for everyone.&lt;&#x2F;p&gt;
&lt;p&gt;Code splitting is very powerful but not that useful if you are just starting: worry about it when your codebase is bigger. If you
are using React, the &lt;a href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;code-splitting.html&quot;&gt;documentation page on code splitting&lt;&#x2F;a&gt; is very well written and should
answer most questions on how to actually use it.&lt;&#x2F;p&gt;
&lt;p&gt;To use code splitting with TypeScript, you will also need to change &lt;code&gt;module&lt;&#x2F;code&gt; in &lt;code&gt;tsconfig.json&lt;&#x2F;code&gt; to &lt;code&gt;esnext&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;analyzing-bundles&quot;&gt;Analyzing bundles&lt;a class=&quot;zola-anchor&quot; href=&quot;#analyzing-bundles&quot; aria-label=&quot;Anchor link for: analyzing-bundles&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Once you have your bundle(s), a useful step is to actually check what they contain and whether there is fat that can be trimmed.
&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;webpack-bundle-analyzer&quot;&gt;webpack-bundle-analyzer&lt;&#x2F;a&gt; is shining for that usecase.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add webpack-bundle-analyzer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We only need to analyze bundles once in a while so I like to create a new script for it &lt;code&gt;&quot;analyze&quot;: &quot;cross-env NODE_ENV=production ANALYZE=true webpack&quot;,&lt;&#x2F;code&gt; and
only instantiate the plugin when that &lt;code&gt;ANALYZE&lt;&#x2F;code&gt; environment variable is set to &lt;code&gt;true&lt;&#x2F;code&gt;.
Running &lt;code&gt;analyze&lt;&#x2F;code&gt; will give you a treemap visualisation of each package used and their size, with and without gzip.&lt;&#x2F;p&gt;
&lt;p&gt;Using this tool, it becomes very easy to notice some packages taking way more spaces than they should. The most classic examples I&#x27;ve seen
personally are not removing &lt;a href=&quot;https:&#x2F;&#x2F;momentjs.com&#x2F;&quot;&gt;Momentjs&lt;&#x2F;a&gt; locales for an English only site and having the full &lt;a href=&quot;https:&#x2F;&#x2F;lodash.com&#x2F;&quot;&gt;Lodash&lt;&#x2F;a&gt; while
only using one or two functions.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;41190ae6e279c84c8614d5d77656b8afaed1dea3&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;41190ae6e279c84c8614d5d77656b8afaed1dea3&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;extracting-css&quot;&gt;Extracting CSS&lt;a class=&quot;zola-anchor&quot; href=&quot;#extracting-css&quot; aria-label=&quot;Anchor link for: extracting-css&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you added Sass by following the previous article and looked at the bundle analyzer results, you might have noticed that the app bundle contains CSS. That&#x27;s because
we&#x27;ve inlined them in our configuration via &lt;code&gt;style-loader&lt;&#x2F;code&gt; and need to extract it to a separate CSS file in production environment instead using a plugin.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add mini-css-extract-plugin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The change is pretty straightforward: we load the &lt;code&gt;MiniCssExtractPlugin&lt;&#x2F;code&gt; loader instead of &lt;code&gt;style-loader&lt;&#x2F;code&gt; in production and
add the plugin to the plugin list. The plugin can be set for every environment as it will not do anything unless the loader
is also used.&lt;&#x2F;p&gt;
&lt;p&gt;Running &lt;code&gt;build:prod&lt;&#x2F;code&gt; will now create a CSS file as well as a sourcemap in the &lt;code&gt;dist&lt;&#x2F;code&gt; folder. I&#x27;m not 100% sure the sourcemap
is accurate as I have never used them for CSS.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;e97a368d67e88f2c1adb9b018b8a4164fdfd3283&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;e97a368d67e88f2c1adb9b018b8a4164fdfd3283&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you followed the articles or just cloned the repository, you should be in a good place to start actually building your project.
It might look complicated compared to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;facebook&#x2F;create-react-app&quot;&gt;create-react-app&lt;&#x2F;a&gt; but this is a minimal setup
that you understand and that only has things what you need. Well, being a JavaScript project it still pulls way too many dependencies just for that but
it&#x27;s a good start.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting up a React+TypeScript frontend with Webpack — Part 1</title>
          <pubDate>Tue, 29 Oct 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/react-typescript-webpack-1/</link>
          <guid>https://www.vincentprouillet.com/blog/react-typescript-webpack-1/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/react-typescript-webpack-1/">&lt;p&gt;Starting a new frontend project is tiresome. My usual process is to look at the last project I worked on,
try to extract all the base dependencies and configuration and see if a Hello World still works after upgrading everything.
This problem might be alleviated with tools such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;facebook&#x2F;create-react-app&quot;&gt;create-react-app&lt;&#x2F;a&gt; but
I personally prefer not having an abstraction layer for the configuration as it is something I always end up having to
tweak. Even when using frameworks like &lt;a href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;&quot;&gt;Next.js&lt;&#x2F;a&gt; where the configuration is hidden, you still need
to make some changes and understanding &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;&quot;&gt;Webpack&lt;&#x2F;a&gt; goes a long way in the frontend world.&lt;&#x2F;p&gt;
&lt;p&gt;The article is split in two parts:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Getting a local dev environment set up: this article&lt;&#x2F;li&gt;
&lt;li&gt;Making it production ready: &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;react-typescript-webpack-2&#x2F;&quot;&gt;part 2&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The focus for these article is to set up a build process for modern browsers with TypeScript: I am not going to pick any library other than React.
State management, testing framework etc are all up to you. The slight
exception to that rule is that I will use &lt;a href=&quot;https:&#x2F;&#x2F;sass-lang.com&#x2F;&quot;&gt;Sass&lt;&#x2F;a&gt; to handle the styling rather than alternatives such as Less&#x2F;CSS-in-JS.
Once again, this is up to you. If you prefer to use JS for your CSS, just skip the last section of this article.&lt;&#x2F;p&gt;
&lt;p&gt;I am also going to use &lt;a href=&quot;https:&#x2F;&#x2F;yarnpkg.com&#x2F;lang&#x2F;en&#x2F;&quot;&gt;yarn&lt;&#x2F;a&gt; as package manager but &lt;code&gt;npm&lt;&#x2F;code&gt; will work as well.&lt;&#x2F;p&gt;
&lt;p&gt;You can view the end result in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;typescript-setup&quot;&gt;TypeScript setup&lt;a class=&quot;zola-anchor&quot; href=&quot;#typescript-setup&quot; aria-label=&quot;Anchor link for: typescript-setup&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;&quot;&gt;TypeScript&lt;&#x2F;a&gt; is the most important tool for me in the frontend world. I first talked about
it on this blog &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;switching-to-typescript&#x2F;&quot;&gt;in 2014&lt;&#x2F;a&gt; and it only got better with time. This is a superset of JavaScript, simply adding
types to it. I don&#x27;t think static typing in dynamic languages are controversial anymore, seeing how &lt;a href=&quot;https:&#x2F;&#x2F;sorbet.org&#x2F;&quot;&gt;many&lt;&#x2F;a&gt; &lt;a href=&quot;http:&#x2F;&#x2F;mypy-lang.org&#x2F;&quot;&gt;languages&lt;&#x2F;a&gt; adopt it.
Static typing is the ultimate form of documentation as it HAS to be correct.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s add it to the project:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add typescript&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will create your project &lt;code&gt;package.json&lt;&#x2F;code&gt; as well as a lock file: &lt;code&gt;yarn.lock&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The next step is to create a &lt;code&gt;tsconfig.json&lt;&#x2F;code&gt;, containing our TypeScript configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;compilerOptions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;module&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;es6&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;moduleResolution&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;node&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;esModuleInterop&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;target&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;es6&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;jsx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;sourceMap&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;strict&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;forceConsistentCasingInFileNames&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noUnusedLocals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noUnusedParameters&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noImplicitReturns&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noFallthroughCasesInSwitch&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;allowUnreachableCode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;include&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;src&#x2F;**&#x2F;*&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s go through them quickly:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;module&lt;&#x2F;code&gt; and &lt;code&gt;esModuleInterop&lt;&#x2F;code&gt;: ensures we use the latest module system and compatible with the Babel ecosystem&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;moduleResolution&lt;&#x2F;code&gt;: look into the &lt;code&gt;node_modules&lt;&#x2F;code&gt; folder for packages&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;target&lt;&#x2F;code&gt;: we target modern browsers so ES6 is fine here. If you need IE11 support, change it to &lt;code&gt;es5&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;jsx&lt;&#x2F;code&gt;: we use React and transpile from TypeScript directly so we use &lt;code&gt;react&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;sourceMap&lt;&#x2F;code&gt;: we want them&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;strict&lt;&#x2F;code&gt;: enable all the strict flags automatically&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All the other options are adding some additional checks, such as not allowing unused variables and a list
of all the options can be found in &lt;a href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;compiler-options.html&quot;&gt;TypeScript documentation&lt;&#x2F;a&gt;.
Lastly, we set up an include path containing &lt;code&gt;&quot;.&#x2F;src&#x2F;**&#x2F;*&quot;&lt;&#x2F;code&gt;: we will put all our code in a &lt;code&gt;src&lt;&#x2F;code&gt; folder.&lt;&#x2F;p&gt;
&lt;p&gt;In this setup, we will only use TypeScript and not &lt;a href=&quot;https:&#x2F;&#x2F;babeljs.io&#x2F;&quot;&gt;Babel&lt;&#x2F;a&gt;. It is possible to use TypeScript for
typechecking only and pass the code to Babel for transpilation but, unless you need a specific Babel plugin, there are no
reasons to do that nowadays. You will have to wait a few more months before getting stage 3 features - not a big deal.&lt;&#x2F;p&gt;
&lt;p&gt;The commit for that step can be seen in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;a083ce1511b15c51d4a8d3ef95f2da05a669758b&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;a083ce1511b15c51d4a8d3ef95f2da05a669758b&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;webpack-setup&quot;&gt;Webpack setup&lt;a class=&quot;zola-anchor&quot; href=&quot;#webpack-setup&quot; aria-label=&quot;Anchor link for: webpack-setup&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start the slightly more complex part! Once you understand how Webpack works, this is actually pretty simple.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;webpack-itself&quot;&gt;Webpack itself&lt;a class=&quot;zola-anchor&quot; href=&quot;#webpack-itself&quot; aria-label=&quot;Anchor link for: webpack-itself&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We first need webpack and its CLI:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add webpack webpack-cli&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s also create a file named &lt;code&gt;webpack.config.js&lt;&#x2F;code&gt; as well as a &lt;code&gt;src&lt;&#x2F;code&gt; folder and an empty &lt;code&gt;index.tsx&lt;&#x2F;code&gt; file in it.
Lastly, I always set up an &lt;a href=&quot;https:&#x2F;&#x2F;editorconfig.org&#x2F;&quot;&gt;.editorconfig&lt;&#x2F;a&gt; file to let text editors sync on how to format things.&lt;&#x2F;p&gt;
&lt;p&gt;A very basic configuration for Webpack doing nothing would be something like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;path&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    context: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;__dirname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    mode: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;development&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    entry: {
&lt;&#x2F;span&gt;&lt;span&gt;        app: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;src&#x2F;index.tsx&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    output: {
&lt;&#x2F;span&gt;&lt;span&gt;        path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;__dirname&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;dist&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        filename: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;app.[hash].js&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    devtool: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;eval-source-map&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    resolve: {
&lt;&#x2F;span&gt;&lt;span&gt;        extensions: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.jsx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.ts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.tsx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    module: {
&lt;&#x2F;span&gt;&lt;span&gt;        rules: []
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This means we are using &lt;code&gt;src&#x2F;index.tsx&lt;&#x2F;code&gt; as the entry point (e.g. what Webpack is loading) and the output will be created in
the &lt;code&gt;dist&lt;&#x2F;code&gt; directory, containing the hash in the filename for caching reasons.
We use &lt;code&gt;eval-source-map&lt;&#x2F;code&gt; as &lt;code&gt;devtool&lt;&#x2F;code&gt; value as this is the best setting for development, You can see all the different values
in the &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;configuration&#x2F;devtool&#x2F;#devtool&quot;&gt;Webpack devtool documentation&lt;&#x2F;a&gt;. Finally, we let Webpack know that
it should try to load imports with no extensions as &lt;code&gt;.js&lt;&#x2F;code&gt;, &lt;code&gt;.jsx&lt;&#x2F;code&gt;, .&lt;code&gt;ts&lt;&#x2F;code&gt; and &lt;code&gt;.tsx&lt;&#x2F;code&gt; (so we can do &lt;code&gt;import something from &quot;..&#x2F;index&quot;&lt;&#x2F;code&gt;) and add a script to build our project.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;package.json b&#x2F;package.json
&lt;&#x2F;span&gt;&lt;span&gt;index eb50375..1bc2df7 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;--- a&#x2F;package.json
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;+++ b&#x2F;package.json
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -1,4 +1,8 @@
&lt;&#x2F;span&gt;&lt;span&gt; {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+  &amp;quot;scripts&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+    &amp;quot;build&amp;quot;: &amp;quot;webpack&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+  },
&lt;&#x2F;span&gt;&lt;span&gt;   &amp;quot;devDependencies&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;     &amp;quot;typescript&amp;quot;: &amp;quot;^3.6.4&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;     &amp;quot;webpack&amp;quot;: &amp;quot;^4.41.2&amp;quot;,
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running that script (&lt;code&gt;yarn build&lt;&#x2F;code&gt;) will create the &lt;code&gt;dist&lt;&#x2F;code&gt; folder containing a file with a name like &lt;code&gt;app.5730f5cdf8cb70458282.js&lt;&#x2F;code&gt;.
Since we didn&#x27;t write any code yet, it currently only contains Webpack&#x27;s own code.&lt;&#x2F;p&gt;
&lt;p&gt;The commit for those changes is in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;48fc3f342c3f4f53c30af1b457d90351f442b4f0&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;48fc3f342c3f4f53c30af1b457d90351f442b4f0&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;loaders-and-plugin&quot;&gt;Loaders and plugin&lt;a class=&quot;zola-anchor&quot; href=&quot;#loaders-and-plugin&quot; aria-label=&quot;Anchor link for: loaders-and-plugin&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In this article we are aiming for a simple SPA without SSR so let&#x27;s setup a super basic React application in order to actually bundle something.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add react react-dom
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add @types&#x2F;react @types&#x2F;react-dom&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;React doesn&#x27;t ship the TypeScript type definitions by default so we need to download them manually.&lt;&#x2F;p&gt;
&lt;p&gt;The simplest React code we can write in &lt;code&gt;src&#x2F;index.tsx&lt;&#x2F;code&gt; is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;React &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ReactDOM &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react-dom&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ReactDOM&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;Hello world&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;h1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;document&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;getElementById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;root&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will render the React app in the HTML element with id &lt;code&gt;root&lt;&#x2F;code&gt;, which, as some of you might have noticed, doesn&#x27;t exist yet.&lt;&#x2F;p&gt;
&lt;p&gt;There is a plugin that will handle the HTML for us: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jantimon&#x2F;html-webpack-plugin&quot;&gt;html-webpack-plugin&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add html-webpack-plugin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We are using this plugin here because we do not need to display dynamic &lt;code&gt;&amp;lt;meta&amp;gt;&lt;&#x2F;code&gt; attributes depending on the page. If you
need it for SEO or other reasons, you will need to use another approach, likely involving some kind of SSR.&lt;&#x2F;p&gt;
&lt;p&gt;Enabling it in our &lt;code&gt;webpack.config.js&lt;&#x2F;code&gt; is trivial:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;webpack.config.js b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span&gt;index 2267dfd..bcbe494 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;--- a&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;+++ b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -1,4 +1,5 @@
&lt;&#x2F;span&gt;&lt;span&gt; const path = require(&amp;quot;path&amp;quot;);
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+const HtmlWebpackPlugin = require(&amp;quot;html-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;     context: __dirname,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -17,4 +18,7 @@ &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;color:#c23f31;&quot;&gt;module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;     module: {
&lt;&#x2F;span&gt;&lt;span&gt;         rules: []
&lt;&#x2F;span&gt;&lt;span&gt;     },
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+    plugins: [
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        new HtmlWebpackPlugin({ template: &amp;quot;index.html&amp;quot; }),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+    ]
&lt;&#x2F;span&gt;&lt;span&gt; };
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The template can be something very basic such as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#494949;&quot;&gt;&amp;lt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;DOCTYPE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#494949;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;html lang=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;en&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;meta charset=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;UTF-8&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;meta name=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;viewport&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;content=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;meta http-equiv=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;X-UA-Compatible&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;content=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;ie=edge&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;title&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;My project&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;&#x2F;title&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&#x2F;head&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;div id=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;root&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;&amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&#x2F;body&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&#x2F;html&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Do remember that the &lt;code&gt;id&lt;&#x2F;code&gt; of a container needs to match the one set in &lt;code&gt;index.tsx&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Trying to run the build against at this point will give you an error about a missing loader for this type of file, referring
to our &lt;code&gt;index.tsx&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The commit for the above is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;3af8820f98b3645381edfb99792700922823394b&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;3af8820f98b3645381edfb99792700922823394b&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Loaders are the way Webpack knows what to do with each filetype. In our case, we have these TypeScript files but we didn&#x27;t tell
Webpack what to do with them: we need to add a loader for it. In the case of TypeScript, we will add a plugin in addition.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add ts-loader fork-ts-checker-webpack-plugin&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The reason we use a loader and a plugin is performance: the loader will only perform transpilation while the plugin
will typecheck in another thread.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;webpack.config.js b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span&gt;index bcbe494..cd657ff 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;--- a&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;+++ b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -1,5 +1,6 @@
&lt;&#x2F;span&gt;&lt;span&gt; const path = require(&amp;quot;path&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; const HtmlWebpackPlugin = require(&amp;quot;html-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+const ForkTsCheckerWebpackPlugin = require(&amp;quot;fork-ts-checker-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;     context: __dirname,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -16,9 +17,19 @@ &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;color:#c23f31;&quot;&gt;module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;         extensions: [&amp;quot;.js&amp;quot;, &amp;quot;.jsx&amp;quot;, &amp;quot;.ts&amp;quot;, &amp;quot;.tsx&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;     },
&lt;&#x2F;span&gt;&lt;span&gt;     module: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-        rules: []
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        rules: [
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                test: &#x2F;\.(ts|tsx)$&#x2F;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                loader: &amp;quot;ts-loader&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                options: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                    &#x2F;&#x2F; We use ForkTsCheckerWebpackPlugin for typechecking
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                    transpileOnly: true
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+                }
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            },
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        ]
&lt;&#x2F;span&gt;&lt;span&gt;     },
&lt;&#x2F;span&gt;&lt;span&gt;     plugins: [
&lt;&#x2F;span&gt;&lt;span&gt;         new HtmlWebpackPlugin({ template: &amp;quot;index.html&amp;quot; }),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        new ForkTsCheckerWebpackPlugin(),
&lt;&#x2F;span&gt;&lt;span&gt;     ]
&lt;&#x2F;span&gt;&lt;span&gt; };
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Those changes are in the commit &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;a3fdbade49deaffd4b5cd762021920efad0fbc8c&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;a3fdbade49deaffd4b5cd762021920efad0fbc8c&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Running &lt;code&gt;yarn build&lt;&#x2F;code&gt; again will now bundle our code: just open &lt;code&gt;dist&#x2F;index.html&lt;&#x2F;code&gt; to check for yourself. While opening that folder,
you might have noticed that there are many &lt;code&gt;.js&lt;&#x2F;code&gt; files in there: we never actually cleaned that folder.
Nothing a Webpack plugin (&lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;clean-webpack-plugin&quot;&gt;clean-webpack-plugin&lt;&#x2F;a&gt;) or a &lt;code&gt;rm -rf dist&#x2F;&lt;&#x2F;code&gt; in the script cannot solve.&lt;&#x2F;p&gt;
&lt;p&gt;The commit for the plugin way is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;42ca8ba4f4ec0312d26defef610f7652a26ce40c&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;42ca8ba4f4ec0312d26defef610f7652a26ce40c&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;webpack-dev-server&quot;&gt;webpack-dev-server&lt;a class=&quot;zola-anchor&quot; href=&quot;#webpack-dev-server&quot; aria-label=&quot;Anchor link for: webpack-dev-server&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We have our build working now but it builds from scratch every time, which sucks.
Thankfully, Webpack has some nice tooling with its dev server allowing Hot Modules Replacement (HMR): live reloading of your code
without having to refresh.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add webpack-dev-server&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And update &lt;code&gt;webpack.config.js&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;webpack.config.js b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span&gt;index 9bf647a..3b576bd 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;--- a&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;+++ b&#x2F;webpack.config.js
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -1,4 +1,5 @@
&lt;&#x2F;span&gt;&lt;span&gt; const path = require(&amp;quot;path&amp;quot;);
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+const webpack = require(&amp;quot;webpack&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; const HtmlWebpackPlugin = require(&amp;quot;html-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; const ForkTsCheckerWebpackPlugin = require(&amp;quot;fork-ts-checker-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt; const { CleanWebpackPlugin } = require(&amp;quot;clean-webpack-plugin&amp;quot;);
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;font-style:italic;color:#f8f8f8;&quot;&gt;@@ -29,9 +30,21 @@ &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#234561;color:#c23f31;&quot;&gt;module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;         },
&lt;&#x2F;span&gt;&lt;span&gt;         ]
&lt;&#x2F;span&gt;&lt;span&gt;     },
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+    devServer: {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        contentBase: path.join(__dirname, &amp;quot;dist&amp;quot;),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        port: 9000,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        hot: true,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        historyApiFallback: true,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        overlay: true,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        stats: &amp;quot;minimal&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+    },
&lt;&#x2F;span&gt;&lt;span&gt;     plugins: [
&lt;&#x2F;span&gt;&lt;span&gt;         new HtmlWebpackPlugin({ template: &amp;quot;index.html&amp;quot; }),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#6c1d15;color:#f8f8f8;&quot;&gt;-        new ForkTsCheckerWebpackPlugin(),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        new ForkTsCheckerWebpackPlugin({
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            &#x2F;&#x2F; For the dev server overlay to work
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+            async: false,
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        }),
&lt;&#x2F;span&gt;&lt;span&gt;         new CleanWebpackPlugin(),
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#3b5a37;color:#f8f8f8;&quot;&gt;+        new webpack.HotModuleReplacementPlugin(),
&lt;&#x2F;span&gt;&lt;span&gt;     ]
&lt;&#x2F;span&gt;&lt;span&gt; };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And add a new script, &lt;code&gt;dev&lt;&#x2F;code&gt; with a value of &lt;code&gt;webpack-dev-server&lt;&#x2F;code&gt;. Running &lt;code&gt;yarn dev&lt;&#x2F;code&gt; will now run a local server on
port 9000 that will also recompiles on change and reload the components without causing a refresh: try it for yourself
by changing the text &lt;code&gt;index.tsx&lt;&#x2F;code&gt; while having the page open.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s backtrack a bit to explain the &lt;code&gt;devServer&lt;&#x2F;code&gt; options though. As always, the complete documentation is available
on &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;configuration&#x2F;dev-server&#x2F;&quot;&gt;Webpack documentation&lt;&#x2F;a&gt; but the key options there are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hot&lt;&#x2F;code&gt;: yes please, we do want HMR&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;historyApiFallback&lt;&#x2F;code&gt;: if your app is a SPA with navigation you will need that option enabled to not see 404s&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;overlay&lt;&#x2F;code&gt;: display errors as an overlay on the app, useful to ensure you&#x27;re not missing it out from the terminal&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;stats&lt;&#x2F;code&gt;: Webpack is very noisy by default, this turns the output level to something more reasonable&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For the changes in the &lt;code&gt;plugins&lt;&#x2F;code&gt; section, we do disable &lt;code&gt;async&lt;&#x2F;code&gt; of &lt;code&gt;ForkTsCheckerWebpackPlugin&lt;&#x2F;code&gt; to have overlay still
work as well as adding the builtin HMR plugin.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;ed546d935487f4e4350e5267261b2c6a7bac015b&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;ed546d935487f4e4350e5267261b2c6a7bac015b&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At this point we have a functional working setup but there are a couple of tools we can add to improve the code quality.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;auto-formatting&quot;&gt;Auto-formatting&lt;a class=&quot;zola-anchor&quot; href=&quot;#auto-formatting&quot; aria-label=&quot;Anchor link for: auto-formatting&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;prettier.io&#x2F;&quot;&gt;Prettier&lt;&#x2F;a&gt; is a very nice tool to format pretty much every language used in the JavaScript world.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add prettier&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Prettier is opinionated and as such has &lt;a href=&quot;https:&#x2F;&#x2F;prettier.io&#x2F;docs&#x2F;en&#x2F;options.html&quot;&gt;few options&lt;&#x2F;a&gt; to configure. The only change I like to make is setting the maximum
number of characters in a line to be 100 instead of the default 80. It can be configured directly from &lt;code&gt;package.json&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;prettier&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;printWidth&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As well as a new script &lt;code&gt;fmt&lt;&#x2F;code&gt;: &lt;code&gt;prettier --write &#x27;src&#x2F;**&#x2F;**.ts*&#x27;&lt;&#x2F;code&gt; that will format all TypeScript files in the &lt;code&gt;src&lt;&#x2F;code&gt; folder.
The main issue is that for these tools to matter, they need to be enforced. An easy way to do that is via a commit hook, using
something like &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;husky&quot;&gt;husky&lt;&#x2F;a&gt;. As with &lt;code&gt;prettier&lt;&#x2F;code&gt;, we can configure &lt;code&gt;husky&lt;&#x2F;code&gt; easily in &lt;code&gt;package.json&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;husky&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;hooks&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;pre-commit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;yarn fmt:check&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;where &lt;code&gt;fmt:check&lt;&#x2F;code&gt; is a new script that only checks whether there are files needing to be formatted: &lt;code&gt;prettier --list-different &#x27;src&#x2F;**&#x2F;**.ts*&#x27;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The next time someones working on that project calls &lt;code&gt;git commit&lt;&#x2F;code&gt;, this command will be ran before and will fail if any file
needs formatting. Don&#x27;t forget to run this command in your CI as well!&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;a464f58d80af0aa45be55bd3df87bb00d74ed958&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;a464f58d80af0aa45be55bd3df87bb00d74ed958&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;linting&quot;&gt;Linting&lt;a class=&quot;zola-anchor&quot; href=&quot;#linting&quot; aria-label=&quot;Anchor link for: linting&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The last code quality tooling we are going to add is &lt;a href=&quot;https:&#x2F;&#x2F;eslint.org&#x2F;&quot;&gt;ESLint&lt;&#x2F;a&gt;. Setting it up requires a few more
dependencies than the other tools as we need to make it work with React, Prettier and Typescript.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add eslint eslint-config-prettier eslint-plugin-react @typescript-eslint&#x2F;eslint-plugin @typescript-eslint&#x2F;parser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The configuration is kept in a file named &lt;code&gt;.estlintrc.js&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;path&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;exports &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  parser: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;@typescript-eslint&#x2F;parser&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  extends: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;plugin:react&#x2F;recommended&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;plugin:@typescript-eslint&#x2F;recommended&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;prettier&#x2F;@typescript-eslint&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  parserOptions: {
&lt;&#x2F;span&gt;&lt;span&gt;    project: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;__dirname&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;tsconfig.json&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    tsconfigRootDir: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;__dirname&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    ecmaVersion: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2018&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    sourceType: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;module&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  rules: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; e.g. &amp;quot;@typescript-eslint&#x2F;explicit-function-return-type&amp;quot;: &amp;quot;off&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&#x2F;prop-types&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  settings: {
&lt;&#x2F;span&gt;&lt;span&gt;    react: {
&lt;&#x2F;span&gt;&lt;span&gt;      version: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;detect&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Tells eslint-plugin-react to automatically detect the version of React to use
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Essentially we tell ESLint that we are going to use TypeScript and what configuration to use for the parser,
enable the recommended lints for React and TypeScript and lastly we add &lt;code&gt;prettier&#x2F;@typescript-eslint&lt;&#x2F;code&gt; to disable all rules
conflicting with &lt;code&gt;prettier&lt;&#x2F;code&gt;. I also disable the &lt;code&gt;react&#x2F;prop-types&lt;&#x2F;code&gt; linting rule as we are using TypeScript and actually
have strongly typed props already.&lt;&#x2F;p&gt;
&lt;p&gt;The linting script in our case is &lt;code&gt;&quot;lint&quot;: &quot;eslint &#x27;src&#x2F;**&#x2F;**.ts*&#x27;&quot;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;f30c07b1564457e55f9110c32ca766caf060b34d&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;f30c07b1564457e55f9110c32ca766caf060b34d&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-sass-handling&quot;&gt;Bonus: Sass handling&lt;a class=&quot;zola-anchor&quot; href=&quot;#bonus-sass-handling&quot; aria-label=&quot;Anchor link for: bonus-sass-handling&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can skip that section if you prefer using some CSS-in-JS.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; yarn add sass-loader node-sass style-loader css-loader&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you might have understood already, we need to add some &lt;em&gt;loaders&lt;&#x2F;em&gt; to handle Sass files in our Webpack config:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c49a39;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#108f3d;&quot;&gt;\.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c49a39;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;[ac]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c49a39;&quot;&gt;ss&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c49a39;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Creates `style` nodes from JS strings injected in our index.html
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;style-loader&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Translates CSS into CommonJS
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;css-loader&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Compiles Sass to CSS
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;sass-loader&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    ]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then you need to tell Webpack to load the root file by importing it into your &lt;code&gt;index.tsx&lt;&#x2F;code&gt; file, in my case
&lt;code&gt;import &quot;.&#x2F;style&#x2F;app.scss&quot;;&lt;&#x2F;code&gt;. If you added &lt;code&gt;scss&lt;&#x2F;code&gt; to &lt;code&gt;resolve.extensions&lt;&#x2F;code&gt; in the Webpack configuration, you can skip the &lt;code&gt;.scss&lt;&#x2F;code&gt;.
Live reload will continue to work with Sass editing: you can change the background colour and see it immediately reflected
in your browser.&lt;&#x2F;p&gt;
&lt;p&gt;This setup will not solve the classic CSS specificity issues where some changes in a component might affect another component, unless
you are well disciplined with naming classes such as using &lt;a href=&quot;http:&#x2F;&#x2F;getbem.com&#x2F;&quot;&gt;BEM&lt;&#x2F;a&gt;. One nice automatic solution to that
are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;css-modules&#x2F;css-modules&quot;&gt;CSS modules&lt;&#x2F;a&gt; but that will likely be a separate article.&lt;&#x2F;p&gt;
&lt;p&gt;Prettier also works with Sass so we can just add &lt;code&gt;.scss&lt;&#x2F;code&gt; file to the format script and it will work automatically.&lt;&#x2F;p&gt;
&lt;p&gt;The commit is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;055c4785da3d167a18137a90c4ca887fddf7feed&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webpack-react-typescript&#x2F;commit&#x2F;055c4785da3d167a18137a90c4ca887fddf7feed&lt;&#x2F;a&gt;, I&#x27;ve also
run Prettier manually on the configuration files to have consistent formatting throughout the project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With that, we have a working development setup. It is not production and deployment ready however and that is what we will look at
in &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;react-typescript-webpack-2&#x2F;&quot;&gt;the second part&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Storing secrets in a git repository with git-crypt</title>
          <pubDate>Sun, 18 Aug 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/storing-secrets-gitcrypt/</link>
          <guid>https://www.vincentprouillet.com/blog/storing-secrets-gitcrypt/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/storing-secrets-gitcrypt/">&lt;p&gt;One of the most common issues working on a web service is where to store your secrets. When just starting, it&#x27;s likely that
they will be directly in the repository itself whether it happens accidentally or not. Having your production secrets
in the clear in the repository is obviously a bad thing and all those secrets should be rotated.&lt;&#x2F;p&gt;
&lt;p&gt;There are multiple ways to store your secrets securely:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;store them in the environment of the servers like Heroku&#x2F;Aptible do&lt;&#x2F;li&gt;
&lt;li&gt;store them as CI&#x2F;CD variables like in Gitlab and bake them in Docker images&lt;&#x2F;li&gt;
&lt;li&gt;use tools like &lt;a href=&quot;https:&#x2F;&#x2F;www.vaultproject.io&#x2F;&quot;&gt;Vault&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you are on a PaaS with an easy way to set secrets as environment variables, you can stop reading this article and just use that.&lt;&#x2F;p&gt;
&lt;p&gt;However, if you&#x27;re a small team using a more traditional deployment encrypting secrets in the repository itself is worth
considering. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AGWA&#x2F;git-crypt&quot;&gt;git-crypt&lt;&#x2F;a&gt; can be used for that. To quote the repository:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;git-crypt enables transparent encryption and decryption of files in a git repository.
Files which you choose to protect are encrypted when committed, and decrypted when checked out.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AGWA&#x2F;git-crypt&quot;&gt;git-crypt&lt;&#x2F;a&gt; is available through many package managers.&lt;&#x2F;p&gt;
&lt;p&gt;Once you have &lt;code&gt;git-crypt&lt;&#x2F;code&gt; and are in your repository, run &lt;code&gt;git-crypt init&lt;&#x2F;code&gt; to generate a key.&lt;&#x2F;p&gt;
&lt;p&gt;The next step is to create a &lt;code&gt;.gitattributes&lt;&#x2F;code&gt;, a file similar to &lt;code&gt;.gitignore&lt;&#x2F;code&gt;, with the following content:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;gitignore&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-gitignore &quot;&gt;&lt;code class=&quot;language-gitignore&quot; data-lang=&quot;gitignore&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;secrets&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;* filter=git-crypt diff=git-crypt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This means that everything inside a folder named &lt;code&gt;secrets&lt;&#x2F;code&gt; will be encrypted. It is recommended to use the &lt;code&gt;**&#x2F;*&lt;&#x2F;code&gt; approach as
it would be easy to forget to encrypt some files otherwise.
Now that your &lt;code&gt;.gitattributes&lt;&#x2F;code&gt; file is in place, you can start adding secrets in that folder. Since you have the secrets
already unlocked, you will see the file in your &lt;code&gt;git diff&lt;&#x2F;code&gt; or &lt;code&gt;git show&lt;&#x2F;code&gt;.
You can check which files are encrypted before pushing by running the &lt;code&gt;git-crypt status -e&lt;&#x2F;code&gt; command:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;~&#x2F;C&#x2F;p&#x2F;gitcrypttest&lt;&#x2F;span&gt;&lt;span&gt; (master&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;…&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;[1]&lt;&#x2F;span&gt;&lt;span&gt; $ git-crypt status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; -e
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;encrypted:&lt;&#x2F;span&gt;&lt;span&gt; secrets&#x2F;.envrc
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you push it to a remote repository and try to view a file in that directory using the web browser for example, you will notice they are just binary data.
Anyone getting access to the repository will not be able to see them unless they can unlock git-crypt. That also includes you working from
a different computer or your team members.&lt;&#x2F;p&gt;
&lt;p&gt;Clearly, we need to be able to unlock the secrets in more than one computer. There are two ways to go about it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;GPG&lt;&#x2F;li&gt;
&lt;li&gt;creating a symmetric key&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can read more about the GPG on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AGWA&#x2F;git-crypt#using-git-crypt&quot;&gt;git-crypt&#x27;s README&lt;&#x2F;a&gt;, I will use the symmetric
key for this article.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how you generate a symmetric key:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;$ git-crypt export-key &#x2F;path&#x2F;to&#x2F;output&#x2F;file
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will generate a binary key that, after &lt;code&gt;base64&lt;&#x2F;code&gt;&#x27;ing, can be stored in your password manager for example and shared securely
with team mates. And that&#x27;s where the issue with this approach lies: some people might store the key insecurely which would be
equivalent to storing your secrets in the clear.&lt;&#x2F;p&gt;
&lt;p&gt;For a small team that can be trusted however, this is an easy approach that can work for quite some time!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Managing environment variables with direnv</title>
          <pubDate>Thu, 30 May 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/direnv/</link>
          <guid>https://www.vincentprouillet.com/blog/direnv/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/direnv/">&lt;p&gt;Following the &lt;a href=&quot;https:&#x2F;&#x2F;12factor.net&#x2F;config&quot;&gt;Twelve-Factors app&lt;&#x2F;a&gt;, most web applications
are storing configuration in environment variables rather than in a repository. It is possible
to store secrets securely in a repository with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;AGWA&#x2F;git-crypt&quot;&gt;git-crypt&lt;&#x2F;a&gt; but that will likely be the topic
of another article.&lt;&#x2F;p&gt;
&lt;p&gt;While in CI and on servers loading those environment variables happen automatically, loading them on your computer is problematic. The
most popular approach is &lt;code&gt;dotenv&lt;&#x2F;code&gt;: you store your environment variables in a &lt;code&gt;.env&lt;&#x2F;code&gt; file and a library in your project will look for
it and load them. A &lt;code&gt;.env&lt;&#x2F;code&gt; file sample:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;REDIS_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;blabla
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Make sure this is not used in other environments
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;APP_SECRET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;hunter2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;TRIAL_DAYS_LENGTH&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;14
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And, for example in the JavaScript implementation, they can be loaded by adding &lt;code&gt;require(&#x27;dotenv&#x27;).config()&lt;&#x2F;code&gt; to your project, as early as possible.
As you can see, this adds a dependency to your project that is only used in development: in CI variables are likely to be set in the CI itself and in production
by whatever provisioning tool you are using.&lt;&#x2F;p&gt;
&lt;p&gt;Enter &lt;a href=&quot;https:&#x2F;&#x2F;direnv.net&#x2F;&quot;&gt;direnv&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It starts the same way as &lt;code&gt;dotenv&lt;&#x2F;code&gt;: you write your variables in a &lt;code&gt;.envrc&lt;&#x2F;code&gt; file which is almost identical to the example above: entries have an &lt;code&gt;export&lt;&#x2F;code&gt; keyword first.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;REDIS_URL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;blabla
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Make sure this is not used in other environments
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;APP_SECRET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;hunter2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;TRIAL_DAYS_LENGTH&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;14
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What sets it apart is how it loads the variables.
You first need to install &lt;code&gt;direnv&lt;&#x2F;code&gt;, which is a cross-platform binary available on most package managers as well as set up a hook in your bash&#x2F;zsh&#x2F;fish config.
This might sound complex but you only need to do this setup once per computer and it actually takes less than a minute. The hook is fast enough that it
is imperceptible - no need to worry about terminal slowdown.&lt;&#x2F;p&gt;
&lt;p&gt;Now that you have the &lt;code&gt;direnv&lt;&#x2F;code&gt; hook, if you &lt;code&gt;cd&lt;&#x2F;code&gt; to a directory with a &lt;code&gt;.envrc&lt;&#x2F;code&gt; you will see:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; direnv: error .envrc is blocked. Run `&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;direnv&lt;&#x2F;span&gt;&lt;span&gt; allow` to approve its content.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As mentioned in the message, &lt;code&gt;direnv&lt;&#x2F;code&gt; will not automatically load the variables found: you first need to allow the &lt;code&gt;.envrc&lt;&#x2F;code&gt; file. You will need
to run &lt;code&gt;direnv allow&lt;&#x2F;code&gt; every time the &lt;code&gt;.envrc&lt;&#x2F;code&gt; is modified. Since &lt;code&gt;direnv&lt;&#x2F;code&gt; loads the variable automatically, this is needed for security as any
repo could run commands on your machine otherwise.&lt;&#x2F;p&gt;
&lt;p&gt;And that&#x27;s it you&#x27;re done. Every time you will &lt;code&gt;cd&lt;&#x2F;code&gt; in a directory, it will automatically load variables found in &lt;code&gt;.envrc&lt;&#x2F;code&gt; and unload them when moving
out of the directory:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cd my-repo&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;direnv:&lt;&#x2F;span&gt;&lt;span&gt; loading .envrc
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;direnv:&lt;&#x2F;span&gt;&lt;span&gt; export +AWS_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY +RUST_LOG +SENTRY_DSN
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cd ..
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;direnv:&lt;&#x2F;span&gt;&lt;span&gt; unloading
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You get the benefits of environment variables for configuration without having to add a dependency to your project.
I was actually using &lt;code&gt;direnv&lt;&#x2F;code&gt; before hearing about &lt;code&gt;dotenv&lt;&#x2F;code&gt;: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;dbmigrate&#x2F;pull&#x2F;14&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;dbmigrate&#x2F;pull&#x2F;14&lt;&#x2F;a&gt; and I still don&#x27;t really see myself using &lt;code&gt;dotenv&lt;&#x2F;code&gt; over it.
I&#x27;m guessing the advantage of &lt;code&gt;dotenv&lt;&#x2F;code&gt; is if you are launching things from an IDE directly, using Docker or at companies restricting what can be installed on a machine.&lt;&#x2F;p&gt;
&lt;p&gt;If this is not your case, I highly recommend &lt;code&gt;direnv&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Zola 0.6.0: start of multi-lingual sites</title>
          <pubDate>Mon, 25 Mar 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-zola-0-6-0/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-zola-0-6-0/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-zola-0-6-0/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt; is a powerful static site generator (SSG)
inspired by &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; but simpler to use. One of its goals is to try
to do as much as possible at built time: anchors, search, Sass, table of contents, syntax highlighting and more.
It is highly flexible and allows you to do any kind of sites:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a blog like the one you are reading right now&lt;&#x2F;li&gt;
&lt;li&gt;a landing page&lt;&#x2F;li&gt;
&lt;li&gt;a knowledge base&lt;&#x2F;li&gt;
&lt;li&gt;a gitbook: see the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;book&quot;&gt;book theme&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;a documentation site for your library like the one for &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can find an up-to-date comparison of Zola with other SSG &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola#comparisons-with-other-static-site-generators&quot;&gt;on the README&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-new&quot;&gt;What&#x27;s new&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-new&quot; aria-label=&quot;Anchor link for: what-s-new&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As usual, you can find the list of changes in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#060-unreleased&quot;&gt;CHANGELOG&lt;&#x2F;a&gt; but we are going to have a look at the highlights in this post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;multi-lingual-sites&quot;&gt;Multi-lingual sites&lt;a class=&quot;zola-anchor&quot; href=&quot;#multi-lingual-sites&quot; aria-label=&quot;Anchor link for: multi-lingual-sites&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;One of the most requested features is now starting to make its way in Zola!
As a recap, multi-lingual sites are sites available in multiple languages - surprising I know. In terms of Zola, this means
your content is written in multiple languages and that you can switch between them when possible. The approach taken is to have
the language code written in the filename: a &lt;code&gt;blog-post.md&lt;&#x2F;code&gt; can have a &lt;code&gt;blog-post.fr.md&lt;&#x2F;code&gt; for its French version.&lt;&#x2F;p&gt;
&lt;p&gt;A more complete guide is available in the &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;multilingual&#x2F;&quot;&gt;documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Something to keep in mind is that this feature is likely to change in the future as more users use it and report their needs.
There are a few things missing:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;translations inside the templates, using something like &lt;a href=&quot;https:&#x2F;&#x2F;projectfluent.org&#x2F;&quot;&gt;fluent&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;taxonomies are currently language specific, which doesn&#x27;t really work if you want to have a taxonomy called
categories and have &lt;code&gt;en-US&lt;&#x2F;code&gt; and &lt;code&gt;en-GB&lt;&#x2F;code&gt; languages for example as it will conflict&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;performance-improvement&quot;&gt;Performance improvement&lt;a class=&quot;zola-anchor&quot; href=&quot;#performance-improvement&quot; aria-label=&quot;Anchor link for: performance-improvement&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the 0.5.0 release post, I posted a screenshot of Zola building a test site.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is a blog with 10000 pages, paginated by 5 with several taxonomies (one of them having a RSS feed).
Each page contains shortcode, syntax highlighting and, although not used in the templates in this cases, a table of contents.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;releasing-zola-0-6-0&#x2F;huge-blog-old.png&quot; alt=&quot;Building a huge blog with Zola 0.5.0&quot; title=&quot;Building a huge blog with Zola 0.5.0&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In 0.6.0, it is now a bit faster:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;releasing-zola-0-6-0&#x2F;huge-blog-new.png&quot; alt=&quot;Building a huge blog with Zola 0.6.0&quot; title=&quot;Building a huge blog with Zola 0.6.0&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The most important difference is that it is now using about &lt;strong&gt;1&#x2F;4 of the memory&lt;&#x2F;strong&gt; it used to use.&lt;&#x2F;p&gt;
&lt;p&gt;Disabling syntax highlighting runs the same benchmark in about 5 seconds so there isn&#x27;t too much overhead
on the Zola side.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Work is going to continue on the multi-lingual front based on the feedback received.&lt;&#x2F;p&gt;
&lt;p&gt;Another thing will be a &lt;code&gt;zola check&lt;&#x2F;code&gt; command to check that the site is in a good state without building it.
This will check all links (internal and external), duplicate pages, templates, Sass files and report any errors.
This is mostly built to replace the current external link checker which isn&#x27;t very useable in its current state.&lt;&#x2F;p&gt;
&lt;p&gt;I am also trying to move more features related issues to the &lt;a href=&quot;https:&#x2F;&#x2F;zola.discourse.group&#x2F;&quot;&gt;forum&lt;&#x2F;a&gt; in order to keep the repository
issues be mostly bug reports.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, I would also love more help on the documentation side as there were some reports the current structure is confusing.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Gutenberg is out, Zola 0.5.0 is in</title>
          <pubDate>Sat, 17 Nov 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-zola-0-5-0/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-zola-0-5-0/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-zola-0-5-0/">&lt;p&gt;Stop the presses! This is an important update for Gutenberg users - please read on
if you are one of them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Gutenberg&#x2F;Zola&lt;&#x2F;a&gt; is a powerful static site generator (SSG)
inspired by &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; but simpler to use. One of its goals is to try
to do as much as possible at built time: anchors, search, Sass, table of contents, syntax highlighting and more.
It is highly flexible and allows you to do any kind of sites:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a blog like the one you are reading right now&lt;&#x2F;li&gt;
&lt;li&gt;a landing page&lt;&#x2F;li&gt;
&lt;li&gt;a knowledge base&lt;&#x2F;li&gt;
&lt;li&gt;a gitbook: see the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;book&quot;&gt;book theme&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;a documentation site for your library like the one for &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can find an up-to-date comparison of Zola with other SSG &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola#comparisons-with-other-static-site-generators&quot;&gt;on the README&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-bit-of-history-and-changing-name-to-zola&quot;&gt;A bit of history and changing name to Zola&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-bit-of-history-and-changing-name-to-zola&quot; aria-label=&quot;Anchor link for: a-bit-of-history-and-changing-name-to-zola&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The first commit on Gutenberg was done in May 2015 after the first stable version of Rust was released.
The goal for that project was for a friend and I to learn Rust and a SSG was a pretty good fit for that.
Since it was a learning project, we didn&#x27;t really care about name conflict: we expected to be the only
users.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are looking at the date of the first commit in the GitHub repo, you will see 2016.
When I started Gutenberg, there was no templates library I liked at the time so I spent some time
building &lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;&quot;&gt;Tera&lt;&#x2F;a&gt; and deleted the original repo as it was outdated by then.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Gutenberg grew more than anticipated and every time it got posted somewhere, half of the comments were pointing out
the name was not original. Most of the name conflicts were with &lt;a href=&quot;https:&#x2F;&#x2F;www.gutenberg.org&#x2F;&quot;&gt;Project Gutenberg&lt;&#x2F;a&gt; or
some &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BafS&#x2F;Gutenberg&quot;&gt;CSS&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matejlatin&#x2F;Gutenberg&quot;&gt;frameworks&lt;&#x2F;a&gt;. I fully agree
with the people saying it isn&#x27;t original. By that time, Gutenberg was already in many package managers and on &lt;a href=&quot;https:&#x2F;&#x2F;www.netlify.com&#x2F;&quot;&gt;Netlify&lt;&#x2F;a&gt;:
changing name was a bit problematic.&lt;&#x2F;p&gt;
&lt;p&gt;More recently though, a small project called &lt;a href=&quot;https:&#x2F;&#x2F;wordpress.org&#x2F;&quot;&gt;WordPress&lt;&#x2F;a&gt; started advertising their new editor, &lt;a href=&quot;https:&#x2F;&#x2F;wordpress.org&#x2F;gutenberg&#x2F;&quot;&gt;gutenberg&lt;&#x2F;a&gt;.
Being WordPress it will simply eclipse every single search for my Gutenberg and any others: I
already had trouble finding one of the CSS framework named Gutenberg while writing this post since all results were about the WordPress editor, which isn&#x27;t
even released yet at that time.
Another point against the Gutenberg name is that my SO works for a WordPress agency: I hear about it all the time.&lt;&#x2F;p&gt;
&lt;p&gt;I opened &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;377&quot;&gt;an issue&lt;&#x2F;a&gt; to rename the project and ended up choosing &lt;code&gt;zola&lt;&#x2F;code&gt;.
Émile Zola is a very famous French writer - at least in France -, mainly for his &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;J%27Accuse%E2%80%A6!&quot;&gt;J&#x27;accuse&lt;&#x2F;a&gt; letter. His
writing style doesn&#x27;t appeal to everyone but his most famous books are &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;L%27Assommoir&quot;&gt;L&#x27;Assomoir&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Germinal_(novel)&quot;&gt;Germinal&lt;&#x2F;a&gt; if you want to read one.
The name &lt;code&gt;zola&lt;&#x2F;code&gt; itself it very short, easy to pronounce and I couldn&#x27;t find any open-source projects with that name other than an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ZolaApp&quot;&gt;inactive organization&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As the name is brand new, &lt;code&gt;zola&lt;&#x2F;code&gt; will not be available immediately in all package managers and not natively on Netlify as I will talk about a bit more below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;changelog&quot;&gt;Changelog&lt;a class=&quot;zola-anchor&quot; href=&quot;#changelog&quot; aria-label=&quot;Anchor link for: changelog&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;0.5.0 took longer than expected but is packed with bug fixes, new features and improvements. Part of the reason this
version has been so slow to be released is that I wanted to have Netlify support on release since all my sites
are using it. However, since my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;netlify&#x2F;binrc&#x2F;pull&#x2F;19&quot;&gt;PR&lt;&#x2F;a&gt; to add it is not really being looked at, I decided
to stop waiting and release it now.
If you are using the current Gutenberg Netlify support, you can either stay on 0.4.1 or 0.4.2 or download the &lt;code&gt;zola&lt;&#x2F;code&gt; binary manually like
mentioned in &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;deployment&#x2F;netlify&#x2F;#automatic-deploys&quot;&gt;the documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With that said, let&#x27;s have a look at the headlines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;a class=&quot;zola-anchor&quot; href=&quot;#performance&quot; aria-label=&quot;Anchor link for: performance&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A big focus for this release was performance. While fast enough for medium sites (&amp;lt; 500 pages), it was slowing down
and consuming lots of memory when dealing with bigger sites and I knew some of the features
I wanted to add would make the problem even worse. Thanks to the help of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Freaky&quot;&gt;@Freaky&lt;&#x2F;a&gt;, we were
able to improve speed dramatically (4-10x in general) while lowering the memory usage, you can see the evolution in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;420&quot;&gt;the issue&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To give an idea of the speed, there are some benchmarks in the repository but you will need to generate them first: &lt;code&gt;python gen.py&lt;&#x2F;code&gt; in &lt;code&gt;components&#x2F;site&#x2F;benches&lt;&#x2F;code&gt;.
This will generate several sites of various sizes and setup but I&#x27;ll use &lt;code&gt;huge-blog&lt;&#x2F;code&gt; for illustration.
It is a blog with 10000 pages, paginated by 5 with several taxonomies (one of them having a RSS feed).
Each page contains shortcode, syntax highlighting and, although not used in the templates in this cases, a table of contents.
Here are the results on my 4.5 years old laptop:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;releasing-zola-0-5-0&#x2F;huge-blog.png&quot; alt=&quot;Building a huge blog with Zola&quot; title=&quot;Building a huge blog with Zola&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Not too bad. I am pretty sure we can optimize the speed further but I won&#x27;t spend more time on it myself, I would rather
try to lower the memory usage right now, which is still way too high: I believe it used around 3GB during the screenshot above, which is insanely
high.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the time spent is now serializing the data to JSON for usage in &lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;&quot;&gt;Tera&lt;&#x2F;a&gt;.
There is an issue for &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;340&quot;&gt;writing Tera&#x27;s own serde format&lt;&#x2F;a&gt; but I am not sure how it would look like
despite the help of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dtolnay&#x2F;&quot;&gt;@dtolnay&lt;&#x2F;a&gt;. If you have ideas on how to improve on that side, I am very happy to hear them!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ancestry&quot;&gt;Ancestry&lt;a class=&quot;zola-anchor&quot; href=&quot;#ancestry&quot; aria-label=&quot;Anchor link for: ancestry&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Each page and section will now have all their parent sections paths in an &lt;code&gt;ancestors&lt;&#x2F;code&gt; property.
This allows you to easily do breadcrumbs as well as just being able to access the parent section, which wasn&#x27;t possible before.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;loading-csv-toml-json-data&quot;&gt;Loading CSV&#x2F;TOML&#x2F;JSON data&lt;a class=&quot;zola-anchor&quot; href=&quot;#loading-csv-toml-json-data&quot; aria-label=&quot;Anchor link for: loading-csv-toml-json-data&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A new Tera function aptly named &lt;code&gt;load_data&lt;&#x2F;code&gt; has been added.
It can load data from both a local file or a remote URL and convert it to Tera values for usage in templates. As such, this is equivalent
to the feature some SSG call data files but also to &lt;code&gt;getJSON&lt;&#x2F;code&gt; and &lt;code&gt;getCSV&lt;&#x2F;code&gt; in Hugo. Thanks to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;serde-rs&#x2F;serde&quot;&gt;serde&lt;&#x2F;a&gt;
for the super simple and powerful serializations!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;date-from-filename&quot;&gt;Date from filename&lt;a class=&quot;zola-anchor&quot; href=&quot;#date-from-filename&quot; aria-label=&quot;Anchor link for: date-from-filename&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If a page with a &lt;code&gt;YYYY-MM-DD{-,_}&lt;&#x2F;code&gt; format, this date will now automatically be set as the page date, unless
overwritten in the front-matter, and the text after &lt;code&gt;{-,_}&lt;&#x2F;code&gt; will be the slug.
For now, a slug is still required but it seems likely that I will allow a file named &lt;code&gt;YYYY-MM-DD&lt;&#x2F;code&gt; in the future
as, in some cases like meeting minutes, pages do not always have a slug different from the date.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;page-templates&quot;&gt;Page templates&lt;a class=&quot;zola-anchor&quot; href=&quot;#page-templates&quot; aria-label=&quot;Anchor link for: page-templates&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Previously, if you had a site that was for example a landing page + blog, it is likely you would need
to specify the template to use to render a blog post on every page. A bit tedious and not very DRY.
From 0.5.0, you can set the &lt;code&gt;page_template&lt;&#x2F;code&gt; attribute in a section and every pages below that section including subsections pages will use it.
Sub-sections can override it by setting their own &lt;code&gt;page_template&lt;&#x2F;code&gt; and pages by setting &lt;code&gt;template&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;transparent-sections&quot;&gt;Transparent sections&lt;a class=&quot;zola-anchor&quot; href=&quot;#transparent-sections&quot; aria-label=&quot;Anchor link for: transparent-sections&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A commonly requested feature was the YYYY&#x2F;MM&#x2F;DD url scheme. I do not think it is a good
one and explained &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;408#issuecomment-429025773&quot;&gt;why&lt;&#x2F;a&gt; previously.
However, someone had &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;408&quot;&gt;a very good idea&lt;&#x2F;a&gt;: transparent section.&lt;&#x2F;p&gt;
&lt;p&gt;By setting &lt;code&gt;transparent&lt;&#x2F;code&gt; to &lt;code&gt;true&lt;&#x2F;code&gt; in a section front-matter, all its pages will be passed to the parent section and so on,
until a section isn&#x27;t &lt;code&gt;transparent&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In practice, this allows you to do the YYYY&#x2F;MM&#x2F;DD scheme easily:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; 2018
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── 10
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   ├── 10
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   │   ├── 2018-10-10_zola-release.md
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   │   └── _index.md
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   └── _index.md
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── _index.md
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; _index.md
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It is definitely more verbose than what Jekyll or Hugo (setting &lt;code&gt;&#x2F;:year&#x2F;:month&#x2F;:title&#x2F;&lt;&#x2F;code&gt; in the config) but is also more powerful.
As far as I&#x27;m aware, you have no way of selecting the intermediate templates in alternatives or they are sometimes not rendered at all.&lt;&#x2F;p&gt;
&lt;p&gt;For example, if I&#x27;m on &lt;a href=&quot;https:&#x2F;&#x2F;intellij-rust.github.io&#x2F;2018&#x2F;11&#x2F;07&#x2F;changelog-86.html&quot;&gt;https:&#x2F;&#x2F;intellij-rust.github.io&#x2F;2018&#x2F;11&#x2F;07&#x2F;changelog-86.html&lt;&#x2F;a&gt; and try
to want to see all the posts in 2018 (https:&#x2F;&#x2F;intellij-rust.github.io&#x2F;2018&#x2F;), I get a 404. I would be very surprised
if Jekyll doesn&#x27;t handle that but it looks optional at least.&lt;&#x2F;p&gt;
&lt;p&gt;Another advantage is that it isn&#x27;t limited to the data in the front-matter: you can organise it however you want. Want to have a blog, prefix all the blog posts
by &lt;code&gt;&#x2F;blog&#x2F;&lt;&#x2F;code&gt; but paginate it in the homepage? Create a &lt;code&gt;blog&lt;&#x2F;code&gt; section, mark it as &lt;code&gt;transparent&lt;&#x2F;code&gt;, paginate the index and you&#x27;re done.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bug-fixes&quot;&gt;Bug fixes&lt;a class=&quot;zola-anchor&quot; href=&quot;#bug-fixes&quot; aria-label=&quot;Anchor link for: bug-fixes&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Many bugs were fixed, including the big Tera one with macros that was shipped in 0.4.2.
Refer to the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;blob&#x2F;next&#x2F;CHANGELOG.md&quot;&gt;changelog&lt;&#x2F;a&gt; for a full list.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;roadmap&quot;&gt;Roadmap&lt;a class=&quot;zola-anchor&quot; href=&quot;#roadmap&quot; aria-label=&quot;Anchor link for: roadmap&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The first step now will be to get a &lt;a href=&quot;https:&#x2F;&#x2F;www.discourse.org&#x2F;&quot;&gt;Discourse&lt;&#x2F;a&gt; up and running to have a better forum than GitHub and move all discussions&#x2F;feature requests to it in
order to keep the GitHub issues for actual bugs.
The major part of my effort will go towards &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;331&quot;&gt;Tera 1.0.0&lt;&#x2F;a&gt;, with a beta version shipping in a future version
of Zola soonish.&lt;&#x2F;p&gt;
&lt;p&gt;I know I keep saying that on every release post but the focus for the next version will be on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;pull&#x2F;111&quot;&gt;i18n support&lt;&#x2F;a&gt; since some
of the cleanup needed is now done. The RFC will need to be updated to take into account the new features in the last year once a Discourse instance is live.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, if you are looking for a place to get started with Rust, Zola is great for that!
The codebase is relatively small and separated in a few sub-crates making it easy to contribute: you can modify the bit you want to change in the sub-crate,
compile&#x2F;test only that one and once you&#x27;re satisfied check where is the compiler complaining in the rest of the project. I will also help personally anyone wanting to start working on an issue.
This offer is valid for any of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&quot;&gt;my projects&lt;&#x2F;a&gt; and if I don&#x27;t respond for a while, don&#x27;t hesitate to ping me.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Atomic Design in Single Page Applications</title>
          <pubDate>Fri, 26 Oct 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/atomic-design-for-spas/</link>
          <guid>https://www.vincentprouillet.com/blog/atomic-design-for-spas/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/atomic-design-for-spas/">&lt;p&gt;This post will outline how I architect visual part of the SPAs I work on. But first, let me explain the issues I faced that led me there.
I used to put every components in a &lt;code&gt;components&lt;&#x2F;code&gt; folder, which obviously doesn&#x27;t scale and make
it hard finding a specific component. After that, I grouped at least the top level routing
components which made things a bit tidier but was still very unsatisfying.
Looking around for various ways to organise design elements, I found &lt;a href=&quot;http:&#x2F;&#x2F;bradfrost.com&#x2F;blog&#x2F;post&#x2F;atomic-web-design&#x2F;&quot;&gt;Atomic Design&lt;&#x2F;a&gt;.
I am not going to dive deep into explaning what is Atomatic Design as the linked article does a very good job
so I would recommend taking 2 minutes to read it beforehand.&lt;&#x2F;p&gt;
&lt;p&gt;You can find a basic clone of &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;&quot;&gt;HackerNews&lt;&#x2F;a&gt; on GitHub: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&lt;&#x2F;a&gt; made using
the principles described in this post to illustrate this article.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, while the title doesn&#x27;t mention a specific framework, all the samples will use React. The
ideas presented in this post will work equally well in Vue.js or any other component-based
framework.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;self-contained-components&quot;&gt;Self-contained components&lt;a class=&quot;zola-anchor&quot; href=&quot;#self-contained-components&quot; aria-label=&quot;Anchor link for: self-contained-components&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;That is something Vue.js users can get behind!
Each component has now its own folder containing everything it needs to be used, apart from
the other components it is using as we will see in a bit.&lt;&#x2F;p&gt;
&lt;p&gt;What this means is that every component folder looks very similar to something like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;input
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; index.tsx
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; _style.scss
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can replace &lt;code&gt;tsx&lt;&#x2F;code&gt; by &lt;code&gt;js&lt;&#x2F;code&gt; if you are not using &lt;a href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;&quot;&gt;TypeScript&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As you can see, the folder also contains a &lt;code&gt;_style.scss&lt;&#x2F;code&gt;. I prefer to keep my CSS out of JS and since I use &lt;a href=&quot;https:&#x2F;&#x2F;sass-lang.com&#x2F;&quot;&gt;Sass&lt;&#x2F;a&gt;
for other projects without JavaScript, it makes sense to use it. You can use whatever floats your boat though.
Those Sass files are only targeting specific CSS classes of that component, which solves potentially overlapping styling:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-css &quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.input &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;&#x2F; the CSS of the component
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main Sass file will import each component individual style after setting up some base rules like fonts or vertical rhythm as can be seen in
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&#x2F;blob&#x2F;91bb067276006b0ea9d191f753112a5688b7c518&#x2F;src&#x2F;style&#x2F;app.scss&quot;&gt;app.scss&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Since each component has its own folder, it is a good place to keep related code.
For example, if I am using &lt;a href=&quot;https:&#x2F;&#x2F;graphql.org&#x2F;&quot;&gt;GraphQL&lt;&#x2F;a&gt; with &lt;a href=&quot;https:&#x2F;&#x2F;www.apollographql.com&#x2F;&quot;&gt;Apollo&lt;&#x2F;a&gt;, I will also have a &lt;code&gt;queries.tsx&lt;&#x2F;code&gt; in there with the queries
used by the component as well as the types:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gql &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;graphql-tag&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Query&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react-apollo&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;IOrganization&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;..&#x2F;..&#x2F;..&#x2F;types&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ALL_ORGS_QUERY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;gql&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;  query allOrgs {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;    organizations {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;      id
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;      name
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;      slug
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;      members {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;        id
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;        role
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;        user {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;          id
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;          name
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;          timezone
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;        }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;      }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;  }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;IData &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;organizations&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;IOrganization&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;AllOrgsQuery &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;Query&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;IData&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have seen how I organise each individual component, let&#x27;s dive in the Atomic Design part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;atomic-design&quot;&gt;Atomic Design&lt;a class=&quot;zola-anchor&quot; href=&quot;#atomic-design&quot; aria-label=&quot;Anchor link for: atomic-design&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you have read the article introducing Atomic Design, you can probably guess that your &lt;code&gt;components&lt;&#x2F;code&gt; folder is going to look like that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; atoms
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; molecules
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; organisms
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; templates
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; pages
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s look at them in the same order.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;atoms&quot;&gt;Atoms&lt;a class=&quot;zola-anchor&quot; href=&quot;#atoms&quot; aria-label=&quot;Anchor link for: atoms&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Atoms are independent components that do not have any dependencies on other components and that exist in isolation: they
probably don&#x27;t do anything on their own, are mostly composed of &lt;code&gt;props&lt;&#x2F;code&gt; and any action they do is coming from &lt;code&gt;props&lt;&#x2F;code&gt;.
In terms of Redux&#x2F;MobX, all of those components should be dumb&#x2F;pure&#x2F;presentational components.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at one private project atoms, I have:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;button&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;input&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;link&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;tag&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;toast&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;portal&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;icon&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are obviously many more but this should be enough to give you an idea: they are the building blocks
of your site.&lt;&#x2F;p&gt;
&lt;p&gt;Try to have as many of your components in that folder, you will have a much easier time composing interfaces later on.&lt;&#x2F;p&gt;
&lt;p&gt;While atoms can become fairly complex (&lt;code&gt;input&lt;&#x2F;code&gt; for example), they can also be as simple as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&#x2F;blob&#x2F;master&#x2F;src&#x2F;app&#x2F;components&#x2F;atoms&#x2F;spinner&#x2F;index.tsx&quot;&gt;a spinner&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;molecules&quot;&gt;Molecules&lt;a class=&quot;zola-anchor&quot; href=&quot;#molecules&quot; aria-label=&quot;Anchor link for: molecules&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;By themselves, atoms are not very useful and need to be combined to make something useable: &lt;code&gt;input&lt;&#x2F;code&gt; + &lt;code&gt;button&lt;&#x2F;code&gt; giving you a &lt;code&gt;form&lt;&#x2F;code&gt; for example.
That is what molecules are: a combination of 2 or more &lt;code&gt;atoms&lt;&#x2F;code&gt; or, in rarer cases, a smart wrapper around an atom.
Therefore molecules should be dumb components when possible but it isn&#x27;t required.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some of the molecules from the same project:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;form&lt;&#x2F;code&gt; (&lt;code&gt;input&lt;&#x2F;code&gt; + &lt;code&gt;label&lt;&#x2F;code&gt;): using MobX for state but dumb otherwise&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;dialog&lt;&#x2F;code&gt; (&lt;code&gt;overlay&lt;&#x2F;code&gt; + &lt;code&gt;button&lt;&#x2F;code&gt;): dumb&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;toast-container&lt;&#x2F;code&gt; (&lt;code&gt;toast&lt;&#x2F;code&gt;): smart component that will display the currents &lt;code&gt;&amp;lt;Toast&amp;gt;&lt;&#x2F;code&gt; components from the store&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Since the HackerNews repository is a basic example, I didn&#x27;t create enough atoms to have interesting molecules. However, looking at
the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&#x2F;blob&#x2F;master&#x2F;src&#x2F;app&#x2F;components&#x2F;molecules&#x2F;story-item&#x2F;index.tsx&quot;&gt;story-item&lt;&#x2F;a&gt; it is easy to imagine
adding an &lt;code&gt;upvote&lt;&#x2F;code&gt; atom in there.&lt;&#x2F;p&gt;
&lt;p&gt;If you need to use a molecule in another molecule, it probably means it is an organism folder as we will now see.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;organisms&quot;&gt;Organisms&lt;a class=&quot;zola-anchor&quot; href=&quot;#organisms&quot; aria-label=&quot;Anchor link for: organisms&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As with molecules, there are two types of organisms I distinguish here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;components using one or more molecules&lt;&#x2F;li&gt;
&lt;li&gt;business-specific usage of molecule&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A good definition comes from the original article:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Organisms are groups of molecules joined together to form a relatively complex, distinct section of an interface.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As before, here are some examples in my project:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sidebar&lt;&#x2F;code&gt;: uses only one molecule but is a distinct section of the interface&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;login-form&lt;&#x2F;code&gt;: the &lt;code&gt;form&lt;&#x2F;code&gt; molecule set-up with the right fields and the GraphQL login query&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;signup-form&lt;&#x2F;code&gt;: the &lt;code&gt;form&lt;&#x2F;code&gt; molecule set-up with the right fields and the GraphQL signup query&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each form in the app gets its own component, making testing&#x2F;storybook very easy.
Most of those forms components do not have style of their own as they just rely on the &lt;code&gt;form&lt;&#x2F;code&gt; molecule for that.&lt;&#x2F;p&gt;
&lt;p&gt;I do not have any organisms in the HackerNews example.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;templates&quot;&gt;Templates&lt;a class=&quot;zola-anchor&quot; href=&quot;#templates&quot; aria-label=&quot;Anchor link for: templates&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If you look at most SaaS sites, you will notice they use different layouts for different parts of their site:
the most common one being one logged out layout for the login&#x2F;signup forms and the logged in layout
with your dashboard and the actual tool.&lt;&#x2F;p&gt;
&lt;p&gt;I classify those different layouts as templates and you could have for example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;logged-in&lt;&#x2F;code&gt;: a &lt;code&gt;sidebar&lt;&#x2F;code&gt; organism on the left and the &lt;code&gt;child&lt;&#x2F;code&gt; on the rest of the page&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;logged-out&lt;&#x2F;code&gt;: a different background with centered forms&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;settings&lt;&#x2F;code&gt;: some tabs&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of those templates will have their own styling - since that&#x27;s their main use - but potentially also queries: you probably
need to fetch the current user information for a &lt;code&gt;logged-in&lt;&#x2F;code&gt; template so moving that query at the template level will allow each page using it
to automatically fetch the data in a uniform way.&lt;&#x2F;p&gt;
&lt;p&gt;The HackerNews example has only &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&#x2F;blob&#x2F;master&#x2F;src&#x2F;app&#x2F;components&#x2F;templates&#x2F;main&#x2F;index.tsx&quot;&gt;one template&lt;&#x2F;a&gt; as, even
when logged in, the interface doesn&#x27;t change significantly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pages&quot;&gt;Pages&lt;a class=&quot;zola-anchor&quot; href=&quot;#pages&quot; aria-label=&quot;Anchor link for: pages&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Those are the components you use in your router, each of them corresponding to a specific URL.
They are therefore connected to your data stores and will probably have to fetch some data from your server. Most of the time,
these components &lt;code&gt;render&lt;&#x2F;code&gt; method will be wrapped into a template component to ensure consistent styling.&lt;&#x2F;p&gt;
&lt;p&gt;Pages tie all of the elements of Atomic Design we just talked about in a small component. There shouldn&#x27;t be any styling
in those components other than the (rare) occasional divergence.
I also follow a slightly more lax approach to the &quot;one component = one folder&quot; mantra and group sub-routes in the same folder as the main route
for easy discovery.&lt;&#x2F;p&gt;
&lt;p&gt;Example of pages components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;login&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;home&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;user-settings&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;verify-email&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can view the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&#x2F;blob&#x2F;master&#x2F;src&#x2F;app&#x2F;components&#x2F;pages&#x2F;stories-list&#x2F;index.tsx&quot;&gt;HackerNews homepage&lt;&#x2F;a&gt; for an example.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While some of the rules I introduced seem inflexible, you can bend them so they make sense to you.
For example, I put my &lt;code&gt;overlay&lt;&#x2F;code&gt; component in the atoms folder despite it using the &lt;code&gt;portal&lt;&#x2F;code&gt; atom simply because I kept looking for it in the atoms folder
since I always forget it actually uses &lt;code&gt;portal&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Overall, this structure has been a big improvement for me:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;super easy to find components (if you don&#x27;t have a Go To Definition shortcut)&lt;&#x2F;li&gt;
&lt;li&gt;trivial to test and make storybook stories&lt;&#x2F;li&gt;
&lt;li&gt;self contained enough that I can copy-paste the atoms and molecules folder and have everything working quickly&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As mentioned in the introduction, I wrote a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hn-react&quot;&gt;HN clone&lt;&#x2F;a&gt; as an example for that article.
It is a basic React app following Atomic Design using TypeScript and MobX, as well as the routing
method &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;testing-a-different-spa-routing-update&#x2F;&quot;&gt;I wrote about before&lt;&#x2F;a&gt; if you are curious about how it looks in practice.
In my own projects, I also use &lt;a href=&quot;https:&#x2F;&#x2F;storybook.js.org&#x2F;&quot;&gt;StoryBook&lt;&#x2F;a&gt; to easily have a visual overview of every component but I skipped adding it
for this example app.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>An overview of package management in Python in 2018</title>
          <pubDate>Fri, 07 Sep 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/overview-package-management-python/</link>
          <guid>https://www.vincentprouillet.com/blog/overview-package-management-python/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/overview-package-management-python/">&lt;p&gt;First of all, this article will only talk about package management in the
context of backend applications. I do not maintain any Python library so I am
not able to give insights on that side of the package management story.&lt;&#x2F;p&gt;
&lt;p&gt;I will begin by quickly explaining the most common way of setting up Python projects and handling dependencies
to make sure everyone is on the same page.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-basic-setup&quot;&gt;A basic setup&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-basic-setup&quot; aria-label=&quot;Anchor link for: a-basic-setup&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Pretty much all the Django&#x2F;Flask projects I have seen use the same structure: a &lt;code&gt;requirements&lt;&#x2F;code&gt;
folder containing several &lt;code&gt;.txt&lt;&#x2F;code&gt; files listing the dependencies and their versions.&lt;&#x2F;p&gt;
&lt;p&gt;The folder typically looks like the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; base.txt
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; local.txt
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; prod.txt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;base.txt&lt;&#x2F;code&gt; contains all the essential dependencies:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;boto3==1.6.10
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Django==2.0.3
&lt;&#x2F;span&gt;&lt;span&gt;django-cors-headers==2.2.0
&lt;&#x2F;span&gt;&lt;span&gt;django-filter==1.1.0
&lt;&#x2F;span&gt;&lt;span&gt;django-floppyforms==1.7.0
&lt;&#x2F;span&gt;&lt;span&gt;django-nested-inline==0.3.7
&lt;&#x2F;span&gt;&lt;span&gt;django-storages==1.6.5
&lt;&#x2F;span&gt;&lt;span&gt;djangorestframework==3.7.7
&lt;&#x2F;span&gt;&lt;span&gt;djangorestframework-jsonp==1.0.2
&lt;&#x2F;span&gt;&lt;span&gt;social-auth-app-django==2.1.0
&lt;&#x2F;span&gt;&lt;span&gt;social-auth-core==1.7.0
&lt;&#x2F;span&gt;&lt;span&gt;django-widget-tweaks==1.4.1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While it is not required, every requirement files I have seen were using &lt;code&gt;==&lt;&#x2F;code&gt;, also called requirement pinning, to
depend on the exact versions to ensure reproducibility. Well, as much reproducibility
as a mutable package index allows anyway as we will see later.&lt;&#x2F;p&gt;
&lt;p&gt;Other requirements files will import the &lt;code&gt;base.txt&lt;&#x2F;code&gt; file and then add environment specific dependencies such
as testing libraries or developer tools for &lt;code&gt;local.txt&lt;&#x2F;code&gt; for example.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;-r base.txt
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ipython==6.2.1
&lt;&#x2F;span&gt;&lt;span&gt;flake8==3.5.0
&lt;&#x2F;span&gt;&lt;span&gt;django-extensions==2.0.6
&lt;&#x2F;span&gt;&lt;span&gt;mypy==0.570
&lt;&#x2F;span&gt;&lt;span&gt;factory_boy==2.10.0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But wait! Before being able to install the dependencies, you first need to create
a &lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;tutorial&#x2F;venv.html&quot;&gt;virtual environment&lt;&#x2F;a&gt; if you are not using &lt;a href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;&quot;&gt;Docker&lt;&#x2F;a&gt;. Python 3
has built-in support for virtualenvs and you can create one simply by running &lt;code&gt;python3 -m venv tutorial-env&lt;&#x2F;code&gt;.
To activate it, you need to execute the &lt;code&gt;activate&lt;&#x2F;code&gt; script in the &lt;code&gt;bin&lt;&#x2F;code&gt; folder of the virtualenv.&lt;&#x2F;p&gt;
&lt;p&gt;As this feature is somewhat recent, many virtualenv helpers exist to make it easier to use.
I use &lt;a href=&quot;https:&#x2F;&#x2F;virtualenvwrapper.readthedocs.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;virtualenvwrapper&lt;&#x2F;a&gt; combined with the &lt;code&gt;zsh&lt;&#x2F;code&gt; plugin with
the same name to automatically activate the right virtualenv when I&#x27;m &lt;code&gt;cd&lt;&#x2F;code&gt;ing into a folder that has one.&lt;&#x2F;p&gt;
&lt;p&gt;Now that your virtualenv is setup and activated, you can run &lt;code&gt;pip install -r requirements&#x2F;local.txt&lt;&#x2F;code&gt;
to install your dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;You can list the currently installed packages by running &lt;code&gt;pip freeze&lt;&#x2F;code&gt; in the virtualenv. The downside is that it will also show
all indirect dependencies as well. You can order that list with the &lt;code&gt;-r my_file.txt&lt;&#x2F;code&gt; argument to have your explicit dependencies
at the top but it isn&#x27;t great.&lt;&#x2F;p&gt;
&lt;p&gt;This process works but is quite manual:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;beginners might not know of virtualenvs and how to use them&lt;&#x2F;li&gt;
&lt;li&gt;packages and their versions are edited manually so it is easy to make mistakes
by forgetting to add&#x2F;remove one&lt;&#x2F;li&gt;
&lt;li&gt;not really secure: the package index &lt;a href=&quot;https:&#x2F;&#x2F;pypi.org&quot;&gt;Pypi&lt;&#x2F;a&gt; is mutable so 2.1.0 one day
can be different from 2.1.0 another day and there will no way to know about it. While you can add
hashes to requirements files but I have never seen anyone go through the hassle of doing that themselves&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We can see that when dealing with package management in Python there are two issues: dealing with virtualenvs
and actually managing the packages.&lt;&#x2F;p&gt;
&lt;p&gt;There are plenty of tools that have been built to fix one or both issues; let&#x27;s look at a few of them now.
I will use the list of packages of an actual Django project with about 28 dependencies to test them. While
speed is not THAT important for a package manager, I will only mention it if there is something to be said about it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pip-tools&quot;&gt;pip-tools&lt;a class=&quot;zola-anchor&quot; href=&quot;#pip-tools&quot; aria-label=&quot;Anchor link for: pip-tools&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jazzband&#x2F;pip-tools&quot;&gt;pip-tools&lt;&#x2F;a&gt; only tries to handle the package management part, meaning you still need to use
&lt;code&gt;virtualenvwrapper&lt;&#x2F;code&gt; or something similar to handle virtualenvs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;pip-tools&lt;&#x2F;code&gt; gives you two command line tools: &lt;code&gt;pip-compile&lt;&#x2F;code&gt; and &lt;code&gt;pip-sync&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The easiest way to get started with &lt;code&gt;pip-tools&lt;&#x2F;code&gt; is to create a &lt;code&gt;requirements.in&lt;&#x2F;code&gt; file listing your dependencies:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;boto3
&lt;&#x2F;span&gt;&lt;span&gt;Django
&lt;&#x2F;span&gt;&lt;span&gt;django-cors-headers
&lt;&#x2F;span&gt;&lt;span&gt;django-filter
&lt;&#x2F;span&gt;&lt;span&gt;django-floppyforms
&lt;&#x2F;span&gt;&lt;span&gt;django-nested-inline
&lt;&#x2F;span&gt;&lt;span&gt;django-storages
&lt;&#x2F;span&gt;&lt;span&gt;djangorestframework
&lt;&#x2F;span&gt;&lt;span&gt;djangorestframework-jsonp
&lt;&#x2F;span&gt;&lt;span&gt;social-auth-app-django
&lt;&#x2F;span&gt;&lt;span&gt;social-auth-core
&lt;&#x2F;span&gt;&lt;span&gt;django-widget-tweaks
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;requirements.in&lt;&#x2F;code&gt; file uses the same requirements syntax as &lt;code&gt;requirements.txt&lt;&#x2F;code&gt;:
I could indicate for example that I want &lt;code&gt;Django==2.0.8&lt;&#x2F;code&gt; instead of the latest version.&lt;&#x2F;p&gt;
&lt;p&gt;Running &lt;code&gt;pip-compile&lt;&#x2F;code&gt; in the same folder will create a &lt;code&gt;requirements.txt&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;# This file is autogenerated by pip-compile
&lt;&#x2F;span&gt;&lt;span&gt;# To update, run:
&lt;&#x2F;span&gt;&lt;span&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;#    pip-compile --output-file requirements.txt requirements.in
&lt;&#x2F;span&gt;&lt;span&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;boto3==1.8.7
&lt;&#x2F;span&gt;&lt;span&gt;botocore==1.11.7          # via boto3, s3transfer
&lt;&#x2F;span&gt;&lt;span&gt;certifi==2018.8.24        # via requests
&lt;&#x2F;span&gt;&lt;span&gt;chardet==3.0.4            # via requests
&lt;&#x2F;span&gt;&lt;span&gt;defusedxml==0.5.0         # via python3-openid, social-auth-core
&lt;&#x2F;span&gt;&lt;span&gt;django-cors-headers==2.4.0
&lt;&#x2F;span&gt;&lt;span&gt;django-filter==2.0.0
&lt;&#x2F;span&gt;&lt;span&gt;django-floppyforms==1.7.0
&lt;&#x2F;span&gt;&lt;span&gt;django-nested-inline==0.3.7
&lt;&#x2F;span&gt;&lt;span&gt;django-storages==1.7
&lt;&#x2F;span&gt;&lt;span&gt;django-widget-tweaks==1.4.2
&lt;&#x2F;span&gt;&lt;span&gt;django==2.1.1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;... more lines
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;pip-compile&lt;&#x2F;code&gt; automatically grabbed the latest versions and each line not present
in your &lt;code&gt;requirements.in&lt;&#x2F;code&gt; has a comment indicating why it is here. This is already
a big improvement over &lt;code&gt;pip freeze &amp;gt; requirements.txt&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Remember the packages hashes mentioned before? &lt;code&gt;pip-compile --generate-hashes&lt;&#x2F;code&gt; can automatically computes the hashes and put them
in the generated file:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;txt&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-txt &quot;&gt;&lt;code class=&quot;language-txt&quot; data-lang=&quot;txt&quot;&gt;&lt;span&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;# This file is autogenerated by pip-compile
&lt;&#x2F;span&gt;&lt;span&gt;# To update, run:
&lt;&#x2F;span&gt;&lt;span&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;#    pip-compile --generate-hashes --output-file requirements.txt requirements.in
&lt;&#x2F;span&gt;&lt;span&gt;#
&lt;&#x2F;span&gt;&lt;span&gt;boto3==1.8.7 \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:7b54cc29dde1d4833b082b8ef4062872297cf652ed20b2d485e1cae544c7cdad \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:b181fb87661a4268174ba29cb5efbc9e728202f3f80e00356e8135ce50a3799b
&lt;&#x2F;span&gt;&lt;span&gt;botocore==1.11.7 \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:4d36cb3a0b6308eeb56f69b964b92b9f4609423b07a4979c298dba694a5a4bd6 \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:86d0bf23b5071c6a956ad2a1d5cd8d1d7c997e2c04151f7c8fff50779a8315d8 \
&lt;&#x2F;span&gt;&lt;span&gt;    # via boto3, s3transfer
&lt;&#x2F;span&gt;&lt;span&gt;certifi==2018.8.24 \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638 \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a \
&lt;&#x2F;span&gt;&lt;span&gt;    # via requests
&lt;&#x2F;span&gt;&lt;span&gt;chardet==3.0.4 \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \
&lt;&#x2F;span&gt;&lt;span&gt;    --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \
&lt;&#x2F;span&gt;&lt;span&gt;    # via requests
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;... more lines
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The initial run takes a bit of time but subsequent ones take under a second.&lt;&#x2F;p&gt;
&lt;p&gt;The last thing we need for package management is an easy way to upgrade the packages and
&lt;code&gt;pip-compile&lt;&#x2F;code&gt; provides that out of the box:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pip-compile --upgrade&lt;&#x2F;code&gt; will update all packages&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pip-compile -P PACKAGE_NAME&lt;&#x2F;code&gt; will update only that package&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;An astute reader might have noticed that so far we have only managed to write and update
a text file and have not actually installed anything. That&#x27;s where &lt;code&gt;pip-sync&lt;&#x2F;code&gt; comes into play.&lt;&#x2F;p&gt;
&lt;p&gt;Running &lt;code&gt;pip-sync requirements.txt&lt;&#x2F;code&gt; will compare the packages listed in the file with
what is currently installed, installing the missing packages and uninstalling the packages
not listed. This ensures your virtualenv doesn&#x27;t have extra dependencies and is representing
accurately your requirements file.&lt;&#x2F;p&gt;
&lt;p&gt;The main con I have is pretty silly: why is it &lt;code&gt;pip-compile&lt;&#x2F;code&gt; and &lt;code&gt;pip-sync&lt;&#x2F;code&gt; instead of &lt;code&gt;piptools compile&lt;&#x2F;code&gt;
and &lt;code&gt;piptools sync&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pipenv&quot;&gt;Pipenv&lt;a class=&quot;zola-anchor&quot; href=&quot;#pipenv&quot; aria-label=&quot;Anchor link for: pipenv&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pipenv.readthedocs.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;Pipenv&lt;&#x2F;a&gt; is developed by the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pypa&quot;&gt;Python Packaging Authority&lt;&#x2F;a&gt;,
responsible for developing &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pypa&#x2F;pip&quot;&gt;pip&lt;&#x2F;a&gt; so I had big expectations.&lt;&#x2F;p&gt;
&lt;p&gt;It is inspired by Cargo&#x2F;Bundler&#x2F;Yarn and introduces 2 files that you will recognize if you used any of them before:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;Pipfile&lt;&#x2F;code&gt;: a TOML file listing packages as well as a minimal Python version&lt;&#x2F;li&gt;
&lt;li&gt;a &lt;code&gt;Pipfile.lock&lt;&#x2F;code&gt;: a lockfile in JSON format listing every dependencies version with their hash&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Anytime you want to install the dependencies, Pipenv will read &lt;code&gt;Pipfile.lock&lt;&#x2F;code&gt; and install everything in it.
To make it easier for users, Pipenv also wraps virtualenv, getting rid of the need to have &lt;code&gt;virtualenvwrapper&lt;&#x2F;code&gt;. Pipenv seems
to be only targeting applications, it doesn&#x27;t help if you are packaging a library.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how it works in practice.&lt;&#x2F;p&gt;
&lt;p&gt;The first step is to create a new project: &lt;code&gt;pipenv --python 3.7&lt;&#x2F;code&gt;.
This is a pretty strange command as &lt;code&gt;pipenv&lt;&#x2F;code&gt; only displays the help so I would have expected the command to be &lt;code&gt;pipenv new&lt;&#x2F;code&gt;
rather than some flags. Typically flags in this situation are only there to display information, like &lt;code&gt;pipenv --version&lt;&#x2F;code&gt;.
Pipenv will also create a new virtualenv when running &lt;code&gt;pipenv install&lt;&#x2F;code&gt; if there isn&#x27;t one.&lt;&#x2F;p&gt;
&lt;p&gt;To activate the newly created virtualenv, run &lt;code&gt;pipenv shell&lt;&#x2F;code&gt;. To exit it, simply type &lt;code&gt;exit&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Running &lt;code&gt;pipenv install django&lt;&#x2F;code&gt; will do two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;insert &lt;code&gt;django = &quot;*&quot;&lt;&#x2F;code&gt; in the &lt;code&gt;packages&lt;&#x2F;code&gt; table of the &lt;code&gt;Pipfile&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;download the latest django version, install it in the virtual env and add the hash&#x2F;version in &lt;code&gt;Pipfile.lock&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Putting &lt;code&gt;&quot;*&quot;&lt;&#x2F;code&gt; as a version number is not great: I need to read the &lt;code&gt;Pipfile.lock&lt;&#x2F;code&gt; to see which version was installed
and &lt;code&gt;*&lt;&#x2F;code&gt; is never a good default for a version number, even with a lockfile. Changing it to &lt;code&gt;django = &quot;==2.1.1&quot;&lt;&#x2F;code&gt; which was
the version it installed did the trick.&lt;&#x2F;p&gt;
&lt;p&gt;You can run &lt;code&gt;pipenv sync&lt;&#x2F;code&gt; to do the equivalent of &lt;code&gt;pip-sync&lt;&#x2F;code&gt; from &lt;code&gt;pip-tools&lt;&#x2F;code&gt;, but using &lt;code&gt;Pipfile.lock&lt;&#x2F;code&gt; as the truth this time.
Pipenv actually uses &lt;code&gt;pip-tools&lt;&#x2F;code&gt; for some of its commands under the hood.&lt;&#x2F;p&gt;
&lt;p&gt;A cool feature of Pipenv is &lt;code&gt;pipenv check&lt;&#x2F;code&gt; that will check for security issues in your packages. Running a slightly
outdated version of Django for example gives the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Checking&lt;&#x2F;span&gt;&lt;span&gt; PEP 508 requirements...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Passed!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Checking&lt;&#x2F;span&gt;&lt;span&gt; installed package safety...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;36368:&lt;&#x2F;span&gt;&lt;span&gt; django &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;=2.0.0, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;.0.8 resolved (2.0.5 installed)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;django.middleware.common.CommonMiddleware&lt;&#x2F;span&gt;&lt;span&gt; in Django 1.11.x before 1.11.15 and 2.0.x before 2.0.8 has an Open Redirect. A remote user can redirect the target user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;s browser to an arbitrary site.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0508&#x2F;&quot;&gt;PEP 508&lt;&#x2F;a&gt; is the grammar defining how to write dependencies versions, i.e. the &lt;code&gt;==&lt;&#x2F;code&gt; for example you
have seen before in that article.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Pipenv seems extremely slow: running &lt;code&gt;pipenv install -d&lt;&#x2F;code&gt; the test project with already pinned versions took 166s.
Almost 3 minutes. Running it again right after took 55s. Any action dealing with packages
takes a minimum of 30s on my machine. I did say in the introduction that speed is not too important but Pipenv does test the limit
of that affirmation.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, I found the UX of the whole tool to be pretty awkward or buggy:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pipenv&lt;&#x2F;code&gt; shows an help menu with the various actions but &lt;code&gt;pipenv check --help&lt;&#x2F;code&gt; for example will not tell you
what &lt;code&gt;pipenv check&lt;&#x2F;code&gt; actually does, only what flags are available&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pipenv uninstall&lt;&#x2F;code&gt; is said to &quot;Un-installs a provided package and removes it from Pipfile.&quot; but somehow &lt;code&gt;pipenv uninstall --three &lt;&#x2F;code&gt;
will destroy the current virtualenv and create a new one? It looks like all subcommands have the same flags available to
create&#x2F;recreate a virtualenv instead of having a &lt;code&gt;pipenv new&lt;&#x2F;code&gt; command and avoid this pitfall.&lt;&#x2F;li&gt;
&lt;li&gt;errors are very cryptic: I made a typo while adding a dependency in &lt;code&gt;Pipfile&lt;&#x2F;code&gt; resulting in an invalid TOML variable and all I got was
two tracebacks for a total of 70 lines with the reason on the last line. How about only showing me the reason instead?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pipenv update&lt;&#x2F;code&gt; doesn&#x27;t tell what was actually updated...?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pipenv update django&lt;&#x2F;code&gt; updates all the packages instead of only django....?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;poetry&quot;&gt;poetry&lt;a class=&quot;zola-anchor&quot; href=&quot;#poetry&quot; aria-label=&quot;Anchor link for: poetry&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I believe I first heard of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sdispater&#x2F;poetry&quot;&gt;poetry&lt;&#x2F;a&gt; while looking up &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ambv&#x2F;black&quot;&gt;Black&lt;&#x2F;a&gt; and noticing
a &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0518&#x2F;&quot;&gt;PEP 518&lt;&#x2F;a&gt; introduced &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This PEP specifies how Python software packages should specify what build dependencies they have in order
to execute their chosen build system.
As part of this specification, a new configuration file is introduced for software packages to use to specify
their build dependencies (with the expectation that the same configuration file will be used for future configuration details).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The interesting part of that PEP for the purpose of this article is the &lt;a href=&quot;https:&#x2F;&#x2F;www.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0518&#x2F;#tool-table&quot;&gt;tool section&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than having one config file for each tool like &lt;a href=&quot;http:&#x2F;&#x2F;flake8.pycqa.org&#x2F;en&#x2F;latest&#x2F;index.html#&quot;&gt;Flake8&lt;&#x2F;a&gt; or Black, having
all of them consolidated in one file would be very valuable.&lt;&#x2F;p&gt;
&lt;p&gt;Poetry uses this file to list the dependencies, keeping everything neatly in place.&lt;&#x2F;p&gt;
&lt;p&gt;There are two ways to get started with poetry:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;poetry new SOME_NAME&lt;&#x2F;code&gt;: will create a folder named SOME_NAME with some basic structure setup for a Python project&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;poetry init&lt;&#x2F;code&gt;: an interactive way to setup your project and some dependencies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The downside of the &lt;code&gt;poetry init&lt;&#x2F;code&gt; is that the package search is not that great: searching for &lt;code&gt;django&lt;&#x2F;code&gt; will
return a list of 99 packages, all of them being &lt;code&gt;django-something&lt;&#x2F;code&gt; and not the actual django. I don&#x27;t know what Pypi
offers as an API so maybe it is a limitation of the API but even then, showing 99 results is not very user friendly and
if there is an exact match it should show it to you.
The help text says I can enter the exact package name but typing &lt;code&gt;django&lt;&#x2F;code&gt; tries to autocomplete with the package names
from the list and results in an error:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; django
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;django&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; is invalid
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span&gt; package &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# to add, or the complete package name if it is not listed:
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt; 0] django-bagou
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt; 1] django-maro
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt; 99 packages
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Poetry will also create virtualenv automatically when running &lt;code&gt;install&lt;&#x2F;code&gt;, &lt;code&gt;add&lt;&#x2F;code&gt; or &lt;code&gt;remove&lt;&#x2F;code&gt; with the current version of &lt;code&gt;python&lt;&#x2F;code&gt;:
you cannot currently create a virtualenv with a different version.&lt;&#x2F;p&gt;
&lt;p&gt;You are supposed to activate a shell with &lt;code&gt;poetry shell&lt;&#x2F;code&gt; but it breaks if you using &lt;code&gt;$WORKON_HOME&lt;&#x2F;code&gt; for other tools:
https:&#x2F;&#x2F;github.com&#x2F;sdispater&#x2F;poetry&#x2F;issues&#x2F;214 so I ended up ignoring the virtualenv related commands. I didn&#x27;t spend time looking
into it so there might be an easy fix but it should work out of the box with the rest of the ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;If you try to install a package using &lt;code&gt;poetry add django&lt;&#x2F;code&gt;, you will notice that a &lt;code&gt;pyproject.lock&lt;&#x2F;code&gt; has been created
and that some dependencies were added to &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;tool.poetry.dependencies&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;python &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;3.7&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;django &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.1&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;tool.poetry.dev-dependencies&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;pytest &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^3.0&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As opposed to the other tools, &lt;code&gt;poetry&lt;&#x2F;code&gt; uses the semantic versioning to refer to dependencies versions. If you have used
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&quot;&gt;Cargo&lt;&#x2F;a&gt; in Rust, you will feel at home. The issue is that many Python libraries do not use
semantic versioning so it does feel a bit weird to use in this context.&lt;&#x2F;p&gt;
&lt;p&gt;While trying to clean its cache to test the speed, I ran into a small UX issue.
Poetry offers a command to clean the cache (&lt;code&gt;poetry cache:clear&lt;&#x2F;code&gt;) but it takes a path&#x2F;string to a cache as an argument. A new
user like me will not know what argument to give, especially since the command is not &lt;a href=&quot;https:&#x2F;&#x2F;poetry.eustace.io&#x2F;docs&#x2F;cli&#x2F;&quot;&gt;in the documentation&lt;&#x2F;a&gt;.
Looking into issues, we can see that we can run &lt;code&gt;poetry cache:clear --all pypi&lt;&#x2F;code&gt; but the documentation doesn&#x27;t even mention &lt;code&gt;cache:clear&lt;&#x2F;code&gt; so it&#x27;s not
obvious.&lt;&#x2F;p&gt;
&lt;p&gt;As expected, the initial &lt;code&gt;poetry install&lt;&#x2F;code&gt; takes some time but subsequent runs take under a second.
The UI is very clean and clear about what was installed and their versions.&lt;&#x2F;p&gt;
&lt;p&gt;Poetry also has &lt;code&gt;poetry update&lt;&#x2F;code&gt; to update dependencies and once again makes it clear what has changed.&lt;&#x2F;p&gt;
&lt;p&gt;While this post is focused on backend applications, it is worth mentioning that Poetry apparently handles building and publishing
libraries as well which is a big plus if you are a maintainer: one tool for all purposes!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-opinion&quot;&gt;My opinion&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-opinion&quot; aria-label=&quot;Anchor link for: my-opinion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I have only mentioned 3 tools but there are way more! I wouldn&#x27;t be surprised if Python had the highest
number of tools dedicated to package and environment management of any programming language.
Since all those tools are written in Python themselves you still need to use Pip first to install them, globally sometimes. A better solution
in my opinion would be to write the package manager in a language compiling down to a binary, solving the bootstrapping issue and
being a good example of &lt;a href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;927&#x2F;&quot;&gt;https:&#x2F;&#x2F;xkcd.com&#x2F;927&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To go back to the three tools mentioned in this article, I would recommend either pip-tools or poetry.
My limited usage of Pipenv had too many WTFs to be considered ready for actual use, not even taking into account
its extreme slowness. I&#x27;m not entirely sure why it is the recommended tool other than the virtualenv setup was easy to do because it feels
like an alpha version. If you are using Docker containers instead of virtualenvs, I don&#x27;t see any advantage of Pipenv over pip-tools or Poetry.&lt;&#x2F;p&gt;
&lt;p&gt;I like the simplicity of &lt;code&gt;pip-tools&lt;&#x2F;code&gt;. If you are currently writing &lt;code&gt;requirements.txt&lt;&#x2F;code&gt; by hand, using &lt;code&gt;pip-tools&lt;&#x2F;code&gt; to get
some extra features like hashes is a pretty small step to take which I would recommend right now.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, if &lt;code&gt;pyproject.toml&lt;&#x2F;code&gt; gets adopted by other tools (Flake8, mypy etc), having everything in one file would be very
appealing and poetry would become the best choice at that time. I expect &lt;code&gt;poetry&lt;&#x2F;code&gt; to work with my current use of &lt;code&gt;virtualenvwrapper&lt;&#x2F;code&gt; though,
I don&#x27;t want to convert all my projects to &lt;code&gt;poetry&lt;&#x2F;code&gt; just to make it happy.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Gutenberg 0.4.0: custom taxonomies, image processing and more</title>
          <pubDate>Sat, 04 Aug 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-gutenberg-0-4-0/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-gutenberg-0-4-0/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-gutenberg-0-4-0/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;Gutenberg&lt;&#x2F;a&gt; is a powerful static site engine inspired by &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; but simpler to use.
You can build pretty much any kind of static site with it using markdown:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a basic blog&lt;&#x2F;li&gt;
&lt;li&gt;a landing site&lt;&#x2F;li&gt;
&lt;li&gt;a knowledge base&lt;&#x2F;li&gt;
&lt;li&gt;a gitbook (there is a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;book&quot;&gt;theme&lt;&#x2F;a&gt; for that)&lt;&#x2F;li&gt;
&lt;li&gt;a documentation site&lt;&#x2F;li&gt;
&lt;li&gt;all of the above combined&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can download built binaries from the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;releases&quot;&gt;Github releases page&lt;&#x2F;a&gt; or install
it from &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;getting-started&#x2F;installation&#x2F;&quot;&gt;one of the installation methods&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You should also be able to install it on Ubuntu via Snapcraft once &lt;a href=&quot;https:&#x2F;&#x2F;forum.snapcraft.io&#x2F;t&#x2F;the-rust-plugin-sets-an-invalid-manifest-path&#x2F;6565&#x2F;6&quot;&gt;this bug&lt;&#x2F;a&gt;
is resolved.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-new&quot;&gt;What&#x27;s new&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-new&quot; aria-label=&quot;Anchor link for: what-s-new&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Lots of things! You can view the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md&quot;&gt;CHANGELOG&lt;&#x2F;a&gt; for
a quick overview: I will look at the biggest changes in more details here including the breaking ones and how to migrate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;breaking-custom-taxonomies&quot;&gt;Breaking: custom taxonomies&lt;a class=&quot;zola-anchor&quot; href=&quot;#breaking-custom-taxonomies&quot; aria-label=&quot;Anchor link for: breaking-custom-taxonomies&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Taxonomies are a way to group content together, the most common one in blogs being tags and categories.
Before 0.4.0, you could only have those two taxonomies: &lt;code&gt;tags&lt;&#x2F;code&gt; and &lt;code&gt;categories&lt;&#x2F;code&gt;. You
couldn&#x27;t paginate their pages, have a RSS feed for each or create your own taxonomy.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how to use, and also how to migrate from the point of view of a current user
already using tags&#x2F;categories.&lt;&#x2F;p&gt;
&lt;p&gt;If you are looking for more information, the &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;taxonomies&#x2F;&quot;&gt;docs&lt;&#x2F;a&gt; have been updated — look
for the various taxonomies pages in the side menu.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;updating-the-configuration&quot;&gt;Updating the configuration&lt;a class=&quot;zola-anchor&quot; href=&quot;#updating-the-configuration&quot; aria-label=&quot;Anchor link for: updating-the-configuration&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The first thing to do is open your &lt;code&gt;config.toml&lt;&#x2F;code&gt; and list the taxonomies you want to use:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# You can delete those 2 lines, they aren&amp;#39;t used anymore.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;generate_tags_pages &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;generate_categories_pages &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# And define them like so instead
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;taxonomies &lt;&#x2F;span&gt;&lt;span&gt;= [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# each tag will have its own RSS feed
&lt;&#x2F;span&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;tags&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;rss &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# 5 items per page for a term, you can also customise the pagination path
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# like for the rest of the paginated content
&lt;&#x2F;span&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;categories&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;paginate_by &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Basic definition: no RSS or pagination
&lt;&#x2F;span&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;authors&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;updating-the-templates&quot;&gt;Updating the templates&lt;a class=&quot;zola-anchor&quot; href=&quot;#updating-the-templates&quot; aria-label=&quot;Anchor link for: updating-the-templates&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;We need to change two things regarding the taxonomies templates:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;their location&lt;&#x2F;li&gt;
&lt;li&gt;the main variable they use&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;First we need to move the templates their new location:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tags.html&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;tags&#x2F;list.html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;tag.html&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;tags&#x2F;single.html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;categories.html&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;categories&#x2F;list.html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;category.html&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;categories&#x2F;single.html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In short, Gutenberg is now looking for:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$TAXONOMY_NAME&#x2F;single.html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$TAXONOMY_NAME&#x2F;list.html&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Lastly, the name of the variables available have changed:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;replace &lt;code&gt;tags&lt;&#x2F;code&gt; and &lt;code&gt;categories&lt;&#x2F;code&gt; with &lt;code&gt;terms&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;replace &lt;code&gt;tag&lt;&#x2F;code&gt; and &lt;code&gt;category&lt;&#x2F;code&gt; with &lt;code&gt;term&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;updating-the-content&quot;&gt;Updating the content&lt;a class=&quot;zola-anchor&quot; href=&quot;#updating-the-content&quot; aria-label=&quot;Anchor link for: updating-the-content&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Before, your front-matter would look like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;span&gt;title = &amp;quot;Provisioning and deploying this blog with Ansible&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;description = &amp;quot;Showing off Ansible by example&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;date = 2013-08-17
&lt;&#x2F;span&gt;&lt;span&gt;category = &amp;quot;Devops&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;tags = [&amp;quot;ansible&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An updated version is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;span&gt;title = &amp;quot;Provisioning and deploying this blog with Ansible&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;description = &amp;quot;Showing off Ansible by example&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;date = 2013-08-17
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;[taxonomies]
&lt;&#x2F;span&gt;&lt;span&gt;categories = [&amp;quot;Devops&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;tags = [&amp;quot;ansible&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;+++
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that every taxonomies is now required to take an array of string, whereas before
there could be only a single category.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;breaking-removal-of-order-sorting-renaming-of-next-previous&quot;&gt;Breaking: removal of &lt;code&gt;order&lt;&#x2F;code&gt; sorting &amp;amp; renaming of &lt;code&gt;next&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;previous&lt;&#x2F;code&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#breaking-removal-of-order-sorting-renaming-of-next-previous&quot; aria-label=&quot;Anchor link for: breaking-removal-of-order-sorting-renaming-of-next-previous&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Before 0.4.0, you could sort pages by &lt;code&gt;date&lt;&#x2F;code&gt;, &lt;code&gt;order&lt;&#x2F;code&gt; and &lt;code&gt;weight&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;order&lt;&#x2F;code&gt; and &lt;code&gt;weight&lt;&#x2F;code&gt; were the opposite of each other but the difference of meaning led
to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;338&quot;&gt;confusion&lt;&#x2F;a&gt; when trying to get the &lt;code&gt;previous&lt;&#x2F;code&gt; or the &lt;code&gt;next&lt;&#x2F;code&gt; page.&lt;&#x2F;p&gt;
&lt;p&gt;To make things more explicit two changes have been made:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;order&lt;&#x2F;code&gt; has been removed&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;page.previous&lt;&#x2F;code&gt; and &lt;code&gt;page.next&lt;&#x2F;code&gt; have been renamed depending on the sorting used:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;date&lt;&#x2F;code&gt;: now called &lt;code&gt;page.earlier&lt;&#x2F;code&gt; and &lt;code&gt;page.later&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;weight&lt;&#x2F;code&gt;: now called &lt;code&gt;page.lighter&lt;&#x2F;code&gt; and &lt;code&gt;page.heavier&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;No more &lt;code&gt;&amp;lt;a class=&quot;previous&quot; href=&quot;{{page.next.permalink}}&amp;gt;{{page.next.title}}&amp;lt;&#x2F;a&amp;gt;&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;To fix sites using &lt;code&gt;order&lt;&#x2F;code&gt; or &lt;code&gt;date&lt;&#x2F;code&gt;, you will need to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;change &lt;code&gt;sort_by&lt;&#x2F;code&gt; of the sections to use &lt;code&gt;weight&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;rename &lt;code&gt;order&lt;&#x2F;code&gt; to &lt;code&gt;weight&lt;&#x2F;code&gt; and invert the order of values if necessary&lt;&#x2F;li&gt;
&lt;li&gt;rename &lt;code&gt;next&lt;&#x2F;code&gt; and &lt;code&gt;previous&lt;&#x2F;code&gt; to &lt;code&gt;heavier&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;lighter&lt;&#x2F;code&gt; or &lt;code&gt;later&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;earlier&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This has been implemented by &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;codesections&quot;&gt;Daniel Sockwell&lt;&#x2F;a&gt; as his first ever Rust PR!
Daniel has also made tons of improvements to the documentation, something always appreciated.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;image-processing&quot;&gt;Image processing&lt;a class=&quot;zola-anchor&quot; href=&quot;#image-processing&quot; aria-label=&quot;Anchor link for: image-processing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is the amazing work of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vojtechkral&quot;&gt;Vojtech Kral&lt;&#x2F;a&gt; who also wrote
a thorough &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;image-processing&#x2F;&quot;&gt;documentation page&lt;&#x2F;a&gt; explaining it in detail.&lt;&#x2F;p&gt;
&lt;p&gt;In short: you can now resize images from a template and implementing a gallery is trivial. Open an issue if you have an idea for
some image processing not already in!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shortcodes-fixes&quot;&gt;Shortcodes fixes&lt;a class=&quot;zola-anchor&quot; href=&quot;#shortcodes-fixes&quot; aria-label=&quot;Anchor link for: shortcodes-fixes&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In the previous versions, &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;content&#x2F;shortcodes&#x2F;&quot;&gt;shortcodes&lt;&#x2F;a&gt; were detected with a Regex and built up
while parsing the Markdown which ended up being some of the worst spaghetti code I have ever written.
It lead to numerous bugs as well some valuable features like array arguments to be almost impossible to implement — at least for me.&lt;&#x2F;p&gt;
&lt;p&gt;In Gutenberg 0.4.0, shortcodes are now rendered before Markdown as a separate pass, going through a custom parser specifically written for it.
The rendered shortcodes are then inserted back into the Markdown as raw HTML so the (much simplified) Markdown parser can ignore them.
Thanks to that redesign, all shortcodes feature requests were added easily and every known bug has been fixed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;external-link-checker&quot;&gt;External link checker&lt;a class=&quot;zola-anchor&quot; href=&quot;#external-link-checker&quot; aria-label=&quot;Anchor link for: external-link-checker&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Link rot can be a big issue: some pages might not make sense anymore as a whole if an important link stopped working.&lt;&#x2F;p&gt;
&lt;p&gt;Since Gutenberg already checks internal links, only external links were left to check!
Simply add &lt;code&gt;check_external_links = true&lt;&#x2F;code&gt; to your &lt;code&gt;config.toml&lt;&#x2F;code&gt; and Gutenberg will look up every link in your pages, reporting the ones
not working.&lt;&#x2F;p&gt;
&lt;p&gt;This process is quite slow though so I would recommend enabling it once in a while to check but not all the time.
Keep in mind that some results might be false negatives: crates.io for example &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;crates.io&#x2F;issues&#x2F;788&quot;&gt;always returns a 404&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The main goal for the next version is i18n support. There is an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;pull&#x2F;111&quot;&gt;open RFC&lt;&#x2F;a&gt; to
discuss how the implementation would look like, comments are very welcome.&lt;&#x2F;p&gt;
&lt;p&gt;The main issue left seems to be the translation of the template texts as I&#x27;m not sure how to handle that.
Right now it&#x27;s a dictionary in &lt;code&gt;config.toml&lt;&#x2F;code&gt; but that can grow to be quite large so a separate file for each language
would make sense. Pluralization also becomes an issue.&lt;&#x2F;p&gt;
&lt;p&gt;Once the spec is clearly defined on all points, I would happy to mentor people of any skill level on bits of it: &lt;code&gt;gutenberg&lt;&#x2F;code&gt; is divided in
many sub-crates, making changes pretty self-contained.&lt;&#x2F;p&gt;
&lt;p&gt;As always if you have any feedback or bugs, please open an issue. Hopefully soon Gutenberg will have enough users
to qualify for a free open-source Discourse forum to have a good way to interact with users more efficiently than GitHub issues.
If you are using Gutenberg and are not featured in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;blob&#x2F;master&#x2F;EXAMPLES.md&quot;&gt;the list of sites&lt;&#x2F;a&gt;, please
add yourself!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tera 0.11 is released</title>
          <pubDate>Mon, 22 Jan 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-tera-0-11/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-tera-0-11/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-tera-0-11/">&lt;p&gt;The work on this version started in September 2017 so I&#x27;m pretty happy to finally have this published.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md&quot;&gt;changelog&lt;&#x2F;a&gt; is pretty thorough but the main
highlights are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;whitespace handling&lt;&#x2F;li&gt;
&lt;li&gt;default arguments for macros&lt;&#x2F;li&gt;
&lt;li&gt;tests, global functions calls and macro calls are now expressions and can be combined like so: &lt;code&gt;if x is divisibleby(2) and x &amp;gt; 10&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Quite a few of the changelog items were written by contributors and I hope this trend continues: thanks
to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;upsuper&quot;&gt;@upsuper&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Alex-PK&quot;&gt;@Alex-PK&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jturner314&quot;&gt;@jturner314&lt;&#x2F;a&gt;,
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hoggetaylor&quot;&gt;@hoggetaylor&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alex&quot;&gt;@alex&lt;&#x2F;a&gt; for that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I am satisfied with the current feature set of Tera and am not planning to do significant changes
to the template language itself, which brings us to a 1.0!&lt;&#x2F;p&gt;
&lt;p&gt;I expect most of the changes until then to happen to the Rust side:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ergonomics improvement: I&#x27;m pretty happy with it so would welcome other viewpoints&lt;&#x2F;li&gt;
&lt;li&gt;template rendering performance improvement (reducing the number &lt;code&gt;clone()&lt;&#x2F;code&gt;, etc)&lt;&#x2F;li&gt;
&lt;li&gt;look into the benefits of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;withoutboats&#x2F;failure&quot;&gt;failure&lt;&#x2F;a&gt;
and whether it&#x27;s worth moving from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang-nursery&#x2F;error-chain&quot;&gt;error-chain&lt;&#x2F;a&gt; (no at a first glance)&lt;&#x2F;li&gt;
&lt;li&gt;try to get something like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;211&quot;&gt;streamable templates&lt;&#x2F;a&gt; in or wait if it would be nicer when generators are stable&lt;&#x2F;li&gt;
&lt;li&gt;look into &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;219&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&#x2F;219&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you think you can help with any of the points above, please leave a comment on the associated issue or create a new one.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Gutenberg 0.1.3: themes are in</title>
          <pubDate>Thu, 31 Aug 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-gutenberg-0-1-3/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-gutenberg-0-1-3/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-gutenberg-0-1-3/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;Gutenberg&lt;&#x2F;a&gt; is a powerful static site engine inspired by &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; but simpler to use.
You can build pretty much any kind of static site with it using markdown:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a basic blog&lt;&#x2F;li&gt;
&lt;li&gt;a landing site&lt;&#x2F;li&gt;
&lt;li&gt;a knowledge base&lt;&#x2F;li&gt;
&lt;li&gt;a gitbook&lt;&#x2F;li&gt;
&lt;li&gt;a documentation site&lt;&#x2F;li&gt;
&lt;li&gt;all of the above combined&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can download built binaries from the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;releases&quot;&gt;Github releases page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-new&quot;&gt;What&#x27;s new&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-new&quot; aria-label=&quot;Anchor link for: what-s-new&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The highlight of this release is, as you might have guessed from the title of the post, themes.&lt;&#x2F;p&gt;
&lt;p&gt;Themes are a staple of static site engines: you can use one to get started with your site and only tweak little bits.&lt;&#x2F;p&gt;
&lt;p&gt;As a proof of concept I have ported a famous theme from Jekyll: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;hyde&quot;&gt;hyde&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Since &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt;, the template engine used in Gutenberg,
supports multiple levels of &lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;docs&#x2F;#inheritance&quot;&gt;inheritance&lt;&#x2F;a&gt;, using a theme is almost seamless.
For example, if you want to change the about part of the sidebar of Hyde, it is as easy as adding a &lt;code&gt;index.html&lt;&#x2F;code&gt; in your
&lt;code&gt;templates&lt;&#x2F;code&gt; folder with the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;jinja2&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-jinja2 &quot;&gt;&lt;code class=&quot;language-jinja2&quot; data-lang=&quot;jinja2&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;hyde&#x2F;templates&#x2F;index.html&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;%}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;block &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sidebar_about &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;%}
&lt;&#x2F;span&gt;&lt;span&gt;    Something else
&lt;&#x2F;span&gt;&lt;span&gt;    You can of course render the theme block
&lt;&#x2F;span&gt;&lt;span&gt;    first by calling &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;super&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;}}&lt;&#x2F;span&gt;&lt;span&gt; if wanted.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;endblock &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sidebar_about &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;%}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The architecture chosen for themes is the same as &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;: you have a themes folder in which you download your
themes (via &lt;code&gt;git clone&lt;&#x2F;code&gt; or copy&#x2F;paste) and tell Gutenberg which one to load by adding a &lt;code&gt;theme = &quot;theme_name&quot;&lt;&#x2F;code&gt; line in your &lt;code&gt;config.toml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is the first pass at themes so there are probably features missing or bugs laying around but it&#x27;s ready to be used!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The main focus now will be to get a site up and running for Gutenberg and to put it in various package managers.
If someone reading this is on Mac and has some time to spare, I would love to get it on Homebrew but I don&#x27;t have access to one.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;pull&#x2F;111&quot;&gt;i18n RFC&lt;&#x2F;a&gt; is open. If you are interested
in multi-lingual sites, please have a look and comment!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Releasing Gutenberg 0.1.0</title>
          <pubDate>Fri, 14 Jul 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-gutenberg-0-1-0/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-gutenberg-0-1-0/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-gutenberg-0-1-0/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;Gutenberg&lt;&#x2F;a&gt; is a powerful static site engine inspired by &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; but simpler to use.
You can build pretty much any kind of static site with it using markdown:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a basic blog&lt;&#x2F;li&gt;
&lt;li&gt;a landing site&lt;&#x2F;li&gt;
&lt;li&gt;a knowledge base&lt;&#x2F;li&gt;
&lt;li&gt;a gitbook&lt;&#x2F;li&gt;
&lt;li&gt;a documentation site&lt;&#x2F;li&gt;
&lt;li&gt;all of the above combined&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can download built binaries from the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;releases&quot;&gt;Github releases page&lt;&#x2F;a&gt;. If you were using Gutenberg
through Cargo, I will not upload new versions to crates.io for three reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&#x2F;issues&#x2F;2263&quot;&gt;package for binary crates don&#x27;t use the Cargo.lock&lt;&#x2F;a&gt; which means a dependency having breaking changes
without respecting SemVer will cause the crate to compile but &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;92&quot;&gt;fail at runtime&lt;&#x2F;a&gt; in some cases&lt;&#x2F;li&gt;
&lt;li&gt;I moved to using a Cargo workspace with many small crates to improve iteration speed&lt;&#x2F;li&gt;
&lt;li&gt;with Sass, building Gutenberg has become more involved and it is easier to let user install the already built binary&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As mentioned in &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;announcing-gutenberg&#x2F;&quot;&gt;my article introducing Gutenberg&lt;&#x2F;a&gt;,
my main issue with Hugo is the extremely poor — and I&#x27;m being kind here — template engine it is using.
This was solved in the first release of Gutenberg by using a template engine similar to Jinja2 I wrote: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Another annoying thing was that, since I was using &lt;a href=&quot;http:&#x2F;&#x2F;sass-lang.com&#x2F;&quot;&gt;Sass&lt;&#x2F;a&gt; to write CSS, each static site had to set up
Sass compilation, usually with node&#x2F;yarn&#x2F;gulp.&lt;&#x2F;p&gt;
&lt;p&gt;With Gutenberg 0.1.0, this is solved as it ships with a static version of libsass, the C++ Sass compiler and allows
commits like &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;Keats&#x2F;vincent.is&#x2F;commit&#x2F;2a05cdad4cfd9dac103c9907488ad71518886440&quot;&gt;this one&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s have a look at the current set of features - it&#x27;s pretty packed for a 0.1.0:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;live reload&lt;&#x2F;strong&gt;: when anything changes, Gutenberg will do the minimum work required and will live reload assets (js&#x2F;css&#x2F;images&#x2F;...) if possible&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;syntax highlighting&lt;&#x2F;strong&gt; built-in via &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;trishume&#x2F;syntect&quot;&gt;syntect&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;pagination&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;easy internal linking&lt;&#x2F;strong&gt;: they look like &lt;code&gt;[my article](@&#x2F;blog&#x2F;blabla.md)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;automatic table of contents&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;automatic insertion of anchors on titles&lt;&#x2F;strong&gt;: same as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;READMEs on Github&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;shortcodes&lt;&#x2F;strong&gt;: when you want to insert some HTML in a page but don&#x27;t want to copy the HTML everywhere. For example
&lt;code&gt;{{ youtube(id=&quot;dQw4w9WgXcQ&quot;) }}&lt;&#x2F;code&gt; is a built-in shortcode and will insert the YouTube video for that id.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Sass compilation&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;RSS feed&lt;&#x2F;strong&gt; generation when requested&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tags and categories&lt;&#x2F;strong&gt; generation when requested&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;co-location of assets and content&lt;&#x2F;strong&gt;: when an article has some images and you want to keep them in the same folder&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The current features are pretty much what I wanted to have when I started Gutenberg but that doesn&#x27;t mean there isn&#x27;t anything left to do!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i18n&quot;&gt;i18n&lt;a class=&quot;zola-anchor&quot; href=&quot;#i18n&quot; aria-label=&quot;Anchor link for: i18n&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The discussion started on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;13&quot;&gt;github&lt;&#x2F;a&gt; but as it isn&#x27;t a feature I need, I would
rather have someone else doing it, once there is consensus on the implementation&lt;&#x2F;p&gt;
&lt;h3 id=&quot;alternative-rendering-backends&quot;&gt;Alternative rendering backends&lt;a class=&quot;zola-anchor&quot; href=&quot;#alternative-rendering-backends&quot; aria-label=&quot;Anchor link for: alternative-rendering-backends&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Right now, only markdown is supported. While I definitely don&#x27;t want to have too many backend, &lt;a href=&quot;http:&#x2F;&#x2F;asciidoctor.org&#x2F;docs&#x2F;what-is-asciidoc&#x2F;&quot;&gt;Asciidoc&lt;&#x2F;a&gt;
is one I was interested in and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;32&quot;&gt;antoyo&lt;&#x2F;a&gt; started working on a asciidoc implementation so
it should be coming eventually!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;theme-support&quot;&gt;Theme support&lt;a class=&quot;zola-anchor&quot; href=&quot;#theme-support&quot; aria-label=&quot;Anchor link for: theme-support&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;91&quot;&gt;An issue&lt;&#x2F;a&gt; was opened on Gutenberg to support themes.
While the idea in the issue is IMO too complex, I would like to have some theme support baked in &lt;code&gt;gutenberg&lt;&#x2F;code&gt;,
unless the &lt;code&gt;git clone a-template-repo&lt;&#x2F;code&gt; approach is simpler.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deploying&quot;&gt;Deploying&lt;a class=&quot;zola-anchor&quot; href=&quot;#deploying&quot; aria-label=&quot;Anchor link for: deploying&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A subcommand to upload&#x2F;push the generated site to Github Pages&#x2F;server&#x2F;service could be nice. Maybe this can be resolved
with documentation instead though.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-documentation-site&quot;&gt;A documentation site&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-documentation-site&quot; aria-label=&quot;Anchor link for: a-documentation-site&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Now that things are a bit more stable in Gutenberg, I will start working on a documentation site as Gutenberg Readme is currently not
nearly enough. I&#x27;ve already started one for &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;tree&#x2F;master&#x2F;docs&quot;&gt;Tera&lt;&#x2F;a&gt; and am mostly waiting for design inspiration
before publishing it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;packaging&quot;&gt;Packaging&lt;a class=&quot;zola-anchor&quot; href=&quot;#packaging&quot; aria-label=&quot;Anchor link for: packaging&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Lastly, I think it&#x27;s time to put Gutenberg in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;12&quot;&gt;packages managers&lt;&#x2F;a&gt;. I will probably
put it on &lt;a href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;&quot;&gt;AUR&lt;&#x2F;a&gt; if no one beats me to it but I will need help for the other package managers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As always I welcome any feedback. If you are using Gutenberg, please reply on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;100&quot;&gt;this issue&lt;&#x2F;a&gt; so I
can update the list in the README.&lt;&#x2F;p&gt;
&lt;p&gt;Joyeux 14 Juillet!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A different approach to routing in Single-Page Applications: now in production</title>
          <pubDate>Wed, 24 May 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/testing-a-different-spa-routing-update/</link>
          <guid>https://www.vincentprouillet.com/blog/testing-a-different-spa-routing-update/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/testing-a-different-spa-routing-update/">&lt;p&gt;I&#x27;ve recently wrote &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;testing-a-different-spa-routing&#x2F;&quot;&gt;an article&lt;&#x2F;a&gt; about a
framework agnostic routing approach for SPA and promised to post an update once it is a bit more tested.
I have since moved Proppy from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ReactTraining&#x2F;react-router&quot;&gt;react-router&lt;&#x2F;a&gt; v2 to
the structure described in my article and the result is pretty great!&lt;&#x2F;p&gt;
&lt;p&gt;I did change a few things from it though.&lt;&#x2F;p&gt;
&lt;p&gt;First, the &lt;code&gt;onClick&lt;&#x2F;code&gt; of the &lt;code&gt;Link&lt;&#x2F;code&gt; component was incorrect and has been changed to:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;onClick&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;React&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;MouseEvent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;preventDefault&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;stopPropagation&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; I also changed the props `name` to `to`
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;props&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The store also changed in order to only display an animation after a certain amount of time, avoiding blinking effects:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ts&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-ts &quot;&gt;&lt;code class=&quot;language-ts&quot; data-lang=&quot;ts&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;RouterStore &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable current&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable asyncInProgress &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Only happens if the page wasn&amp;#39;t loaded in less than 500ms
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable showLoadingScreen &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loadingTimeout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;null &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;setCurrent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;current &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;asyncInProgress &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;clearTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loadingTimeout&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;showLoadingScreen &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;startAsyncLoading&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;asyncInProgress &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loadingTimeout &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;showLoadingScreen &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Any component can now observe &lt;code&gt;showLoadingScreen&lt;&#x2F;code&gt; and render a loading effect very easily. 500 milliseconds is probably
too high but I was on a slow connection several thousands of kilometers away from our servers when I tried it.&lt;&#x2F;p&gt;
&lt;p&gt;The only issue we encountered was that some users were faced with a blank page. I was not able
to reproduce it and no errors were reported to our Sentry server. After a couple of unsuccessful debugging
sessions, I noticed something in the &lt;a href=&quot;http:&#x2F;&#x2F;router5.github.io&#x2F;docs&#x2F;router-options.html&quot;&gt;router5 documentation&lt;&#x2F;a&gt;:
the &lt;code&gt;strictQueryParams&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;By default in &lt;code&gt;router5&lt;&#x2F;code&gt;, a route with a path of &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; will not be matched by the URL &lt;code&gt;&#x2F;?_ga=blabla&lt;&#x2F;code&gt;:
turns out this was exactly what was happening for Proppy. I
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;router5&#x2F;router5&#x2F;issues&#x2F;137&quot;&gt;opened an issue&lt;&#x2F;a&gt; to change the default to a saner one and it seems
it will happen, but in the next major version as this is a breaking change. In the meantime,
the paragraph about &lt;code&gt;strictQueryParams&lt;&#x2F;code&gt; got moved to the top in the docs in order to be more visible.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, someone mailed me mentioning &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;LeonardoGentile&#x2F;react-mobx-router5&quot;&gt;react-mobx-router5&lt;&#x2F;a&gt; which
was inspired by &lt;a href=&quot;https:&#x2F;&#x2F;hackernoon.com&#x2F;how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37&quot;&gt;the same article&lt;&#x2F;a&gt;
as this serie of posts. I think a library for this routing approach is overkill. Pretty much all the code needed is in
the &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;testing-a-different-spa-routing&#x2F;&quot;&gt;previous article&lt;&#x2F;a&gt; and is less than 100 LOC that you can customise
any way you want since different apps will have different needs.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A different approach to routing in Single-Page Applications</title>
          <pubDate>Mon, 08 May 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/testing-a-different-spa-routing/</link>
          <guid>https://www.vincentprouillet.com/blog/testing-a-different-spa-routing/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/testing-a-different-spa-routing/">&lt;p&gt;Routing is a big part of most SPA:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;what to render when a user hits a certain URL&lt;&#x2F;li&gt;
&lt;li&gt;whether the user is allowed to reach that page&lt;&#x2F;li&gt;
&lt;li&gt;what data do we need to load before rendering&lt;&#x2F;li&gt;
&lt;li&gt;eventually display some loading screen if necessary.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem&quot; aria-label=&quot;Anchor link for: the-problem&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the React world, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ReactTraining&#x2F;react-router&quot;&gt;React-Router&lt;&#x2F;a&gt; is the standard.
It is a bit infamous as its API has changed drastically for each major version.
Proppy is still using v2 as updating it didn&#x27;t seem to bring much benefit and would take time.
Our only issue with our current setup is that we are not able to make the async transitions the way we would like them to be, but it is
an annoying one from a UX point of view.&lt;&#x2F;p&gt;
&lt;p&gt;After reading &lt;a href=&quot;https:&#x2F;&#x2F;hackernoon.com&#x2F;how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37&quot;&gt;How to decouple state and UI (a.k.a. you don’t need componentWillMount)&lt;&#x2F;a&gt; by the author of &lt;a href=&quot;https:&#x2F;&#x2F;mobx.js.org&quot;&gt;MobX&lt;&#x2F;a&gt;, I realised that routing should be something that is framework-agnostic.
After all, isn&#x27;t routing simply matching a URL state to a function? There is no need for something specific to React, Angular or anything else.&lt;&#x2F;p&gt;
&lt;p&gt;I recently started experimenting with it and I think I found a nice setup.
I haven&#x27;t tried it for a complex app yet though so it is probably lacking in some ways.
I will write another article on how it works in a real app when I have the time to try it in Proppy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-solution&quot; aria-label=&quot;Anchor link for: the-solution&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This example will use React, MobX and &lt;a href=&quot;http:&#x2F;&#x2F;router5.github.io&#x2F;&quot;&gt;router5&lt;&#x2F;a&gt;.
router5 is a nice router library that treats routing state like any application state.
Combined with MobX, you can have a store that will contain the routing state trivially without any framework.
I couldn&#x27;t find an up to date TypeScript definition for router5 so I &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;1b8833b581d01e751048e1f51041817f&quot;&gt;made one&lt;&#x2F;a&gt;.
I&#x27;m not entirely sure whether this is the correct way to write a definition file though so I will wait a bit before making a PR to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DefinitelyTyped&#x2F;DefinitelyTyped&#x2F;&quot;&gt;DefinitelyTyped&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;routing-setup&quot;&gt;Routing setup&lt;a class=&quot;zola-anchor&quot; href=&quot;#routing-setup&quot; aria-label=&quot;Anchor link for: routing-setup&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s take a very simple routing scheme:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Logged-in routes
&lt;&#x2F;span&gt;&lt;span&gt;  {name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;home&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, canActivate: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loggedInRequired&lt;&#x2F;span&gt;&lt;span&gt;, onActivate: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;dashboardStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Logged-out routes
&lt;&#x2F;span&gt;&lt;span&gt;  {name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&#x2F;signin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  {name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signup&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&#x2F;signup&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;  {name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;forgot-password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, path: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&#x2F;forgot-password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;];
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Routes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;home&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signin&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signup&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;forgot-password&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, nothing magical going on: &lt;code&gt;routes&lt;&#x2F;code&gt; is simply an array of routes, which are plain JavaScript objects themselves.
The &lt;code&gt;Routes&lt;&#x2F;code&gt; type has to be manually updated to match the routes but allows compile-time checking of the routes: a worthy trade-off in my eyes.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;canActivate&lt;&#x2F;code&gt; key corresponds to a &lt;a href=&quot;http:&#x2F;&#x2F;router5.github.io&#x2F;docs&#x2F;preventing-navigation.html&quot;&gt;lifecycle function&lt;&#x2F;a&gt; of router5.
router5 will call this function before transitioning to the new state, preventing the transition if necessary.
In that case it is simply the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;loggedInRequired &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fromState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;done&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; userIsLoggedIn can be whatever you need it to be
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;userIsLoggedIn&lt;&#x2F;span&gt;&lt;span&gt;()) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; redirect to signin page if the user isn&amp;#39;t logged in
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;done&lt;&#x2F;span&gt;&lt;span&gt;({redirect: {name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}});
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;onActivate&lt;&#x2F;code&gt; will be explained in a bit.&lt;&#x2F;p&gt;
&lt;p&gt;Before looking at the router setup, let&#x27;s have a quick look at the MobX router store:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;router5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;RouterStore &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable current&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable asyncInProgress &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Called after transition
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;setCurrent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;current &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;asyncInProgress &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;startAsyncLoading&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;asyncInProgress &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;RouterStore&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you haven&#x27;t used MobX before, I heavily recommend it and &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;&quot;&gt;wrote an introduction to it&lt;&#x2F;a&gt; before. In a nutshell,
think of the code above as a simple class that has 2 observable values: &lt;code&gt;current&lt;&#x2F;code&gt; and &lt;code&gt;asyncInProgress&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ok we got our routes and store, we now need to create a router:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Router setup
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;createRouter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routes&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;usePlugin&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;browserPlugin&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;mobxRouterPlugin&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;useMiddleware&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;asyncMiddleware&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routes&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This does a few things. First we create a router using the routes we defined above. We then add 2 plugins:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;browser&lt;&#x2F;code&gt;: a built-in plugin that will update the browser URL and state on route change using the HTML5 history API&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mobxRouterPlugin&lt;&#x2F;code&gt;: a very simple custom made plugin shown below that will upate the store on transition success and error&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;stores&#x2F;router&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Tell MobX which page we&amp;#39;re on
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;mobxRouterPlugin&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Router&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;onTransitionError&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; TODO handle that.
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;onTransitionSuccess&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;setCurrent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;mobxRouterPlugin &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;PluginFactory&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;pluginName &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;MOBX_PLUGIN&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;How errors are handled is really up to you, I&#x27;m focusing on the happy path for that article.&lt;&#x2F;p&gt;
&lt;p&gt;Next up is the &lt;code&gt;asyncMiddleware&lt;&#x2F;code&gt; that handles any pre-loading we need to do.
If a &lt;code&gt;onActivate&lt;&#x2F;code&gt; function on a route is found a route, it assumes it is an async call that returns a promise:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;asyncMiddleware &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Array&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Router&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fromState&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;State&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;done&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;route &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routes&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; do we have a function to call?
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;onActivate&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Tell the store that will load something, might want to have some visual loading effect
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;startAsyncLoading&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;route&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;onActivate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;toState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Fail the transition if the call failed
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;done&lt;&#x2F;span&gt;&lt;span&gt;({code: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;TRANSITION_ERR&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, error: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;done&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In practice, the &lt;code&gt;onActivate&lt;&#x2F;code&gt; method of the &lt;code&gt;home&lt;&#x2F;code&gt; route will be called before transitioning and will only be completed if the call succeeded.
The store will automatically be notified of the start of an async call and of any successful transition: displaying a loading progress becomes straightforward.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we start the router which will automatically use the current URL as the current state as we are using the Browser plugin.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;integrating-react&quot;&gt;Integrating React&lt;a class=&quot;zola-anchor&quot; href=&quot;#integrating-react&quot; aria-label=&quot;Anchor link for: integrating-react&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have the routing is up and running, we need to be able to render components depending on the URL and navigate between pages.
Since all the routing state is in a MobX store, this is simply a matter of having a component observe it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observer
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;App &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;React&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{}, {}&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;current &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;component &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;switch &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;routerStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;current&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Routes&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signup&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;component &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;SignUp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;signin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;component &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;SignIn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;forgot-password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;component &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;ForgotPassword &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; A 404 would be quite blank.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;app-container&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;component&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s it. A simple switch on the name of the current route and we&#x27;re good to go.&lt;&#x2F;p&gt;
&lt;p&gt;There is a small twist for links though: using a basic &lt;code&gt;&amp;lt;a href=&quot;&#x2F;forgot-password&quot;&amp;gt;Forgot?&amp;lt;&#x2F;a&amp;gt;&lt;&#x2F;code&gt; will trigger a full reload, unless there&#x27;s an option I missed.
You will need to use the &lt;code&gt;router.navigate&lt;&#x2F;code&gt; method to navigate instead.
If you are using React you might have a &lt;code&gt;Link&lt;&#x2F;code&gt; component in your project to standardize how links are made and the various styles it can have.
Here&#x27;s an example for a basic one:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;ILinkProps &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Routes&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?: &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;reload&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;refresh&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;boolean&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Link &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;React&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;ILinkProps&lt;&#x2F;span&gt;&lt;span&gt;, {}&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;protected static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;defaultProps &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    options: {},
&lt;&#x2F;span&gt;&lt;span&gt;    params: {},
&lt;&#x2F;span&gt;&lt;span&gt;  };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;props&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Build the end url
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;href &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;buildPath&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;href &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;null&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; tslint:disable-next-line
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&amp;lt;Link&amp;gt; Couldn&amp;#39;t make URL for&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;a href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;onClick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;onClick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.bind(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;)}&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;props&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;children&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;private &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;onClick&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;React&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MouseEvent&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{}&amp;gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;props&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;comboKey &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;metaKey &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;altKey &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ctrlKey &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;shiftKey&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;button &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;comboKey&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;preventDefault&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;router&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;navigate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Link&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which can be used like so:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Link &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;forgot-password&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;Forgotten password?&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The route name will be checked at compile time as well, no more typos!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-end&quot;&gt;The end&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-end&quot; aria-label=&quot;Anchor link for: the-end&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Will this work for complex apps?
I don&#x27;t know but I will certainly try it and report!&lt;&#x2F;p&gt;
&lt;p&gt;Edit: It looks like TypeScript 2.4 will support &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Microsoft&#x2F;TypeScript&#x2F;pull&#x2F;15486&quot;&gt;string enums&lt;&#x2F;a&gt;!
This means we will be able to not duplicate the &lt;code&gt;Routes&lt;&#x2F;code&gt; content and use an enum instead!&lt;&#x2F;p&gt;
&lt;p&gt;Edit2: This approach is now in production! Read up on &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;testing-a-different-spa-routing-update&#x2F;&quot;&gt;the follow-up article&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>This month in my Rust projects</title>
          <pubDate>Sun, 23 Apr 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/working-on-rust-projects-in-april-2017/</link>
          <guid>https://www.vincentprouillet.com/blog/working-on-rust-projects-in-april-2017/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/working-on-rust-projects-in-april-2017/">&lt;p&gt;I have just released v2 of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;jsonwebtoken&quot;&gt;jsonwebtoken&lt;&#x2F;a&gt; and
thought a blog post would be a good way to talk about the progress of jsonwebtoken, Tera and Gutenberg this month.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jsonwebtoken&quot;&gt;jsonwebtoken&lt;a class=&quot;zola-anchor&quot; href=&quot;#jsonwebtoken&quot; aria-label=&quot;Anchor link for: jsonwebtoken&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Version 2 is almost a full rewrite, but trying to keep the UX as good as possible.&lt;&#x2F;p&gt;
&lt;p&gt;The following improvements have been made:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;serde&lt;&#x2F;code&gt; instead of &lt;code&gt;rustc_serialize&lt;&#x2F;code&gt;: no brainer as &lt;code&gt;serde&lt;&#x2F;code&gt; is now usable on stable and reached 1.0.0&lt;&#x2F;li&gt;
&lt;li&gt;RSA signing and verification&lt;&#x2F;li&gt;
&lt;li&gt;Validation&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Validation is the main thing that was missing from version 1 and brings &lt;code&gt;jsonwebtoken&lt;&#x2F;code&gt; to almost parity with libraries
in other languages. It is detailed &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;jsonwebtoken#validation&quot;&gt;in the README&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As an aside, the signature of &lt;code&gt;decode&lt;&#x2F;code&gt; is an example of why I would like to have default arguments in functions.
The signature is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T: DeserializeOwned&amp;gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;validation&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;Validation
&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;TokenData&amp;lt;T&amp;gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But I expect &lt;code&gt;Validation&lt;&#x2F;code&gt; to be the default most of the time so the ideal signature would be something like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T: DeserializeOwned&amp;gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;token&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    validation = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;Validation::&lt;&#x2F;span&gt;&lt;span&gt;default() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; or whatever the syntax would be
&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;TokenData&amp;lt;T&amp;gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And using &lt;code&gt;decode&lt;&#x2F;code&gt; would be cleaner (at least for me):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;token, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;key&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;Validation::default());
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; VS
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;token, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;key&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is a &lt;a href=&quot;https:&#x2F;&#x2F;internals.rust-lang.org&#x2F;t&#x2F;pre-rfc-named-arguments&#x2F;3831&#x2F;234&quot;&gt;pre RFC&lt;&#x2F;a&gt; but not much progress has
been made recently.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks a lot to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mike-engel&quot;&gt;Mike Engel&lt;&#x2F;a&gt; for all the feedback!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tera&quot;&gt;Tera&lt;a class=&quot;zola-anchor&quot; href=&quot;#tera&quot; aria-label=&quot;Anchor link for: tera&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt; is a template engine based on Jinja2&#x2F;Django templates&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Tera has gone through 2 versions in April: 0.9.0 and now 0.10.&lt;&#x2F;p&gt;
&lt;p&gt;0.9 fixed a major bug on Windows and 0.10 updated &lt;code&gt;serde&lt;&#x2F;code&gt; to 1.0, no major changes otherwise: Tera is pretty stable now.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gutenberg&quot;&gt;Gutenberg&lt;a class=&quot;zola-anchor&quot; href=&quot;#gutenberg&quot; aria-label=&quot;Anchor link for: gutenberg&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;Gutenberg&lt;&#x2F;a&gt; is a static site engine&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Lots of progress this month on Gutenberg!&lt;&#x2F;p&gt;
&lt;p&gt;A short list of the biggest new features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Shortcodes inspired by &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;extras&#x2F;shortcodes&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Relative links to your own content: &lt;code&gt;[my last article](.&#x2F;posts&#x2F;an-article.md)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Anchors for titles with an option to insert some HTML to allow behaviour similar to the Github READMEs&lt;&#x2F;li&gt;
&lt;li&gt;Works on Windows!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Next up is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;14&quot;&gt;sorting by weight&#x2F;order&lt;&#x2F;a&gt; and finishing up
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;7&quot;&gt;pagination&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Once those features are in, I will probably spend some time making an actual documentation site to ensure it works for sites
more complex than this blog.&lt;&#x2F;p&gt;
&lt;p&gt;If anyone is knowledgeable about &lt;a href=&quot;http:&#x2F;&#x2F;livereload.com&#x2F;&quot;&gt;Live Reload&lt;&#x2F;a&gt;, I&#x27;d like some help tracking down &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;10&quot;&gt;this issue&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Solving &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;trishume&#x2F;syntect&#x2F;issues&#x2F;20&quot;&gt;this issue in Syntect&lt;&#x2F;a&gt; would also help but I don&#x27;t have time&#x2F;motivation for it myself right now.&lt;&#x2F;p&gt;
&lt;p&gt;Otherwise, Gutenberg mostly needs feedback and even feature request for things I didn&#x27;t consider yet.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Announcing Gutenberg, a static site engine</title>
          <pubDate>Sat, 25 Mar 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/announcing-gutenberg/</link>
          <guid>https://www.vincentprouillet.com/blog/announcing-gutenberg/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/announcing-gutenberg/">&lt;p&gt;&lt;em&gt;Why would we possibly want another static site engine?&lt;&#x2F;em&gt; I already hear some say.
That&#x27;s a very valid question!&lt;&#x2F;p&gt;
&lt;p&gt;I currently have 6 static sites live:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;wearewizards.io (link now dead)&lt;&#x2F;li&gt;
&lt;li&gt;blog.wearewizards.io (link now dead)&lt;&#x2F;li&gt;
&lt;li&gt;vincent.is (link now dead; redirecting to vincentprouillet.com)&lt;&#x2F;li&gt;
&lt;li&gt;proppy.io  (link now dead)&lt;&#x2F;li&gt;
&lt;li&gt;proppy.io&#x2F;blog (link now dead)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pixelspa.com&quot;&gt;pixelspa.com&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Those are all using &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;, except the blog you&#x27;re reading
that moved to Gutenberg a couple of days ago.&lt;&#x2F;p&gt;
&lt;p&gt;Hugo in itself is pretty great: I switched from Pelican for the speed and the stand-alone binary and
stayed for the instant live reload. The huge pain point for me with Hugo is the Go template
engine: I find it so bad that it turns a great experience into a, well, sad experience.&lt;&#x2F;p&gt;
&lt;p&gt;When I first looked at Rust a couple of years ago, I thought that a static site engine could be a nice first
program to write to learn the language. Sadly, I didn&#x27;t find a template engine I liked at the time which
prompted me to write my own, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now that Tera is getting more mature and my frustration with Go templates grows, I started
working again on the static site engine a few weeks ago and it got to a point where it&#x27;s usable.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s called &lt;strong&gt;Gutenberg&lt;&#x2F;strong&gt; and is now available in a 0.0.2 version on crates.io and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;releases&quot;&gt;Github&lt;&#x2F;a&gt;.
It seems AppVeyor is running into some issue with curl so I haven&#x27;t been able to test it on Windows yet but
it should work on Linux and OSX. Binaries are built using the very nice &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;japaric&#x2F;trust&quot;&gt;trust&lt;&#x2F;a&gt; project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-features&quot;&gt;The features&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-features&quot; aria-label=&quot;Anchor link for: the-features&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Gutenberg is opinionated (only TOML for configuration, only Tera for templates, only CommonMark for content) but is flexible
enough to let you build all kinds of sites, from landing pages to knowledge bases, not only blogs.&lt;&#x2F;p&gt;
&lt;p&gt;Features in no particular order are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CommonMark for content&lt;&#x2F;li&gt;
&lt;li&gt;Categories and Tags automatic page creation (easy to disable)&lt;&#x2F;li&gt;
&lt;li&gt;Automatic RSS feed (easy to disable)&lt;&#x2F;li&gt;
&lt;li&gt;Built-in server with fast live reload (a bit buggy currently)&lt;&#x2F;li&gt;
&lt;li&gt;Supports having assets next to the content: no need to put everything in a &lt;code&gt;static&lt;&#x2F;code&gt; folder&lt;&#x2F;li&gt;
&lt;li&gt;Explicit sections that can have their own page and have access to potential subsections: this allows making pages like &lt;a href=&quot;https:&#x2F;&#x2F;easyengine.io&#x2F;tutorials&#x2F;&quot;&gt;https:&#x2F;&#x2F;easyengine.io&#x2F;tutorials&#x2F;&lt;&#x2F;a&gt;
which are a big table of contents over several sections&lt;&#x2F;li&gt;
&lt;li&gt;Good template engine: I&#x27;m a bit biased there&lt;&#x2F;li&gt;
&lt;li&gt;Simple to use: no surprises, everything is explicit except from the default templates used&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;More information is available on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;README&lt;&#x2F;a&gt; and better documentation will come
once the tool is in a more stable shape.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As the 0.0.2 version number indicates, Gutenberg is still a work-in-progress. Despite
that, it fills almost all my static engine needs. There are a few things that will need
to be done before being ready for a 0.1 though:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Find and fix bugs, add tests and benchmarks&lt;&#x2F;li&gt;
&lt;li&gt;Make it faster: parallelize if possible&lt;&#x2F;li&gt;
&lt;li&gt;Pagination: still debating the best way to set up that up in a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&#x2F;issues&#x2F;7&quot;&gt;Github issue&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Add an equivalent to &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;content-management&#x2F;shortcodes&#x2F;#shortcodes-with-markdown&quot;&gt;Hugo shortcodes&lt;&#x2F;a&gt;: they make it easy to embed
some HTML in your markdown without repeating HTML all the time, e.g. a youtube video could be added to a page like so &lt;code&gt;{{ youtube(id=87238) }}&lt;&#x2F;code&gt;
and the renderer will insert whatever template we defined for the &lt;code&gt;youtube&lt;&#x2F;code&gt; shortcode.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And for after the 0.1 release:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in Jupyter renderer would be nice&lt;&#x2F;li&gt;
&lt;li&gt;Proper documentation site&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The current blocker to parallelization is that &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;trishume&#x2F;syntect&quot;&gt;syntect&lt;&#x2F;a&gt;, the
great library I&#x27;m using for syntax highlighting is not thread-safe. There is an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;trishume&#x2F;syntect&#x2F;issues&#x2F;20&quot;&gt;issue&lt;&#x2F;a&gt; open
but I haven&#x27;t had the time or energy to look&#x2F;fix that yet.
It should be fairly straightforward to add Rayon once it&#x27;s fixed, if the benchmarks show a significant difference.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-does-it-look-in-practice&quot;&gt;How does it look in practice&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-does-it-look-in-practice&quot; aria-label=&quot;Anchor link for: how-does-it-look-in-practice&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can have a look at the source of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;vincentprouillet&quot;&gt;this site&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This site is very simple: a blog and a couple of other pages but it should give some ideas.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enjoy&quot;&gt;Enjoy!&lt;a class=&quot;zola-anchor&quot; href=&quot;#enjoy&quot; aria-label=&quot;Anchor link for: enjoy&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Obviously, any feedback is welcome!
While the general design and current features are unlikely to change, I&#x27;d be happy to cover
more use-cases if possible.&lt;&#x2F;p&gt;
&lt;p&gt;Check it out on Github: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gutenberg&quot;&gt;Gutenberg&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My current frontend setup is pretty neat</title>
          <pubDate>Sat, 28 Jan 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/happy-with-frontend-setup/</link>
          <guid>https://www.vincentprouillet.com/blog/happy-with-frontend-setup/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/happy-with-frontend-setup/">&lt;p&gt;From time to time, I see someone on Github forking or starring the Angular boilerplate I made and was using 2 years ago.
As everyone knows, time in JavaScript-land tends to operate in a different scale: something done 2 years ago
is prehistoric and a generation or two of frameworks went by.&lt;&#x2F;p&gt;
&lt;p&gt;I have been using &lt;a href=&quot;https:&#x2F;&#x2F;facebook.github.io&#x2F;react&#x2F;&quot;&gt;React&lt;&#x2F;a&gt; for about a year now, mainly for the frontend
of Proppy and some experiments on the side. While the churn for the libraries during that year
reached a level I had never seen before, it seems to have stabilised a bit.
That or I stopped trying to follow JS news, who knows.&lt;&#x2F;p&gt;
&lt;p&gt;Since many people might suffer from &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Analysis_paralysis&quot;&gt;analysis paralysis&lt;&#x2F;a&gt;, I decided to document
my own choices to help. It will also provide a good laugh for people find that article in a few months&#x2F;years, missing the date
and thinking &quot;people are STILL using X?&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;package-manager&quot;&gt;Package manager&lt;a class=&quot;zola-anchor&quot; href=&quot;#package-manager&quot; aria-label=&quot;Anchor link for: package-manager&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Yes, you can even choose your package manager in JavaScript!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;npm&lt;&#x2F;code&gt; is fine as long as you remember to &lt;a href=&quot;https:&#x2F;&#x2F;docs.npmjs.com&#x2F;cli&#x2F;shrinkwrap&quot;&gt;shrinkwrap&lt;&#x2F;a&gt; and don&#x27;t mind slow installs.&lt;&#x2F;p&gt;
&lt;p&gt;I settled on &lt;a href=&quot;https:&#x2F;&#x2F;yarnpkg.com&#x2F;&quot;&gt;yarn&lt;&#x2F;a&gt;, which does the right thing by default: a lock file and deterministic installs.
Installing packages is also faster than &lt;code&gt;npm&lt;&#x2F;code&gt;, which is a nice bonus. Despite how recent it is, I haven&#x27;t run into bugs yet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;module-bundler&quot;&gt;Module bundler&lt;a class=&quot;zola-anchor&quot; href=&quot;#module-bundler&quot; aria-label=&quot;Anchor link for: module-bundler&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I use &lt;a href=&quot;https:&#x2F;&#x2F;webpack.github.io&#x2F;&quot;&gt;Webpack&lt;&#x2F;a&gt;.
Not much to say here, once you understand how to configure it - and it might take a while - it
is easy to get going (AKA &lt;code&gt;cp ..&#x2F;previous_project&#x2F;webpack.config.js .&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;I also like to use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ampedandwired&#x2F;html-webpack-plugin&quot;&gt;html-webpack-plugin&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;language&quot;&gt;Language&lt;a class=&quot;zola-anchor&quot; href=&quot;#language&quot; aria-label=&quot;Anchor link for: language&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I use &lt;a href=&quot;http:&#x2F;&#x2F;www.typescriptlang.org&#x2F;&quot;&gt;TypeScript&lt;&#x2F;a&gt;.
If you are using plain Javascript or just Babel, I strongly encourage you
to try something that adds types, be it &lt;a href=&quot;http:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;flowtype.org&#x2F;&quot;&gt;Flow&lt;&#x2F;a&gt;, TypeScript or something else like
ScalaJS.&lt;&#x2F;p&gt;
&lt;p&gt;Some people view types as hindrance but they make me so much more productive:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Object shapes and function arguments are documented and is enforced by the compiler&lt;&#x2F;li&gt;
&lt;li&gt;I can do sweeping changes in a codebase without any trouble&lt;&#x2F;li&gt;
&lt;li&gt;Fewer errors: trying to call a function that only takes a &lt;code&gt;string&lt;&#x2F;code&gt; with a potentially &lt;code&gt;undefined&lt;&#x2F;code&gt; argument? Nope&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Elm catch phrase is &quot;no runtime exceptions&quot; but I&#x27;d argue it is also valid for anything sufficiently typed.&lt;&#x2F;p&gt;
&lt;p&gt;The big advantage TypeScript has over the alternatives mentioned is the huge community
and type definitions are available for tons of libraries.&lt;&#x2F;p&gt;
&lt;p&gt;If you are planning to use TypeScript, make sure you start your project with a very strict config.
I use the following for new projects:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;compilerOptions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;target&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;es5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;module&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;commonjs&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;jsx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;experimentalDecorators&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noImplicitAny&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;strictNullChecks&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noUnusedParameters&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;noUnusedLocals&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;sourceMap&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;exclude&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;node_modules&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you are converting a large app to TS, &lt;code&gt;noImplicitAny&lt;&#x2F;code&gt; might be a pain so only
set it to &lt;code&gt;true&lt;&#x2F;code&gt; when done.&lt;&#x2F;p&gt;
&lt;p&gt;I also use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;palantir&#x2F;tslint&quot;&gt;tslint&lt;&#x2F;a&gt; for linting, which comes with a default good config.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;view&quot;&gt;View&lt;a class=&quot;zola-anchor&quot; href=&quot;#view&quot; aria-label=&quot;Anchor link for: view&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned in the introduction, I use React.
I&#x27;ve also been following&#x2F;contributing a bit to &lt;a href=&quot;https:&#x2F;&#x2F;infernojs.org&#x2F;&quot;&gt;Inferno&lt;&#x2F;a&gt; as an alternative but it doesn&#x27;t
have great type definitions, which is a deal breaker for me.&lt;&#x2F;p&gt;
&lt;p&gt;I would also recommend to mostly write your own components if design is an important factor and you have time for it instead
of using third-party components. Unless it&#x27;s something like an admin or a private site, then go ahead and use a material library
or any UI library.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;state-management&quot;&gt;State management&lt;a class=&quot;zola-anchor&quot; href=&quot;#state-management&quot; aria-label=&quot;Anchor link for: state-management&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve settled on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mobxjs&#x2F;mobx&quot;&gt;MobX&lt;&#x2F;a&gt; and am pretty happy about it.
I wrote about &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;&quot;&gt;MobX and going from Redux&lt;&#x2F;a&gt; before so I&#x27;m not going to repeat myself here.&lt;&#x2F;p&gt;
&lt;p&gt;In short: very simple to use, fully typed and not verbose at all.
A pleasure after using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;reactjs&#x2F;redux&quot;&gt;Redux&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing&quot; aria-label=&quot;Anchor link for: testing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m currently using Mocha&#x2F;Chai with &lt;a href=&quot;https:&#x2F;&#x2F;karma-runner.github.io&#x2F;&quot;&gt;Karma&lt;&#x2F;a&gt; in Proppy and it&#x27;s working well.
I have heard good things about recent versions of &lt;a href=&quot;https:&#x2F;&#x2F;facebook.github.io&#x2F;jest&#x2F;&quot;&gt;Jest&lt;&#x2F;a&gt; but haven&#x27;t had the time to look at it yet,
it is the next item on the TO TRY list.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;css&quot;&gt;CSS&lt;a class=&quot;zola-anchor&quot; href=&quot;#css&quot; aria-label=&quot;Anchor link for: css&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I am using &lt;a href=&quot;http:&#x2F;&#x2F;sass-lang.com&#x2F;&quot;&gt;Sass&lt;&#x2F;a&gt; with some postcss plugins like &lt;a href=&quot;http:&#x2F;&#x2F;postcss.org&#x2F;&quot;&gt;Lost&lt;&#x2F;a&gt;
or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;postcss&#x2F;autoprefixer&quot;&gt;Autoprefixer&lt;&#x2F;a&gt;. If you are targeting evergreen browsers, &lt;code&gt;autoprefixer&lt;&#x2F;code&gt; won&#x27;t be needed.&lt;&#x2F;p&gt;
&lt;p&gt;I haven&#x27;t seen a single CSS-in-JS library that convinced me.
The closest was &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;typestyle&#x2F;typestyle&quot;&gt;TypeStyle&lt;&#x2F;a&gt; but is ultimately failing because it has a runtime
overhead. I&#x27;ll address that in the &quot;What am I missing&quot; section later on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prototyping&quot;&gt;Prototyping&lt;a class=&quot;zola-anchor&quot; href=&quot;#prototyping&quot; aria-label=&quot;Anchor link for: prototyping&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I recently discovered &lt;a href=&quot;https:&#x2F;&#x2F;storybook.js.org&#x2F;&quot;&gt;storybook&lt;&#x2F;a&gt;. If you are using React and didn&#x27;t hear about it or tried it,
stop reading this article and go play with it. It&#x27;s that glorious.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than explaining how it works, have a look at &lt;a href=&quot;http:&#x2F;&#x2F;airbnb.io&#x2F;react-dates&#x2F;&quot;&gt;Airbnb date picker storybook&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Storybook allows you to easily develop components and see all their potential usages in one place, as well as showing an always
up-to-date style guide.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-am-i-missing&quot;&gt;What am I missing&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-am-i-missing&quot; aria-label=&quot;Anchor link for: what-am-i-missing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m still missing a few things and welcome any input.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;compile-time-styling&quot;&gt;Compile-time styling&lt;a class=&quot;zola-anchor&quot; href=&quot;#compile-time-styling&quot; aria-label=&quot;Anchor link for: compile-time-styling&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As mentioned before, TypeStyle is really close to a good solution for styling but for me,
a styling tool should have no runtime cost. CSS is working well and having JavaScript do its job doesn&#x27;t really make sense.&lt;&#x2F;p&gt;
&lt;p&gt;I raised an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;typestyle&#x2F;typestyle&#x2F;issues&#x2F;86&quot;&gt;issue&lt;&#x2F;a&gt; for that but it seems rewriting AST is not
available in TypeScript yet so a TS plugin that extracts those call is not doable for now.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;animations&quot;&gt;Animations&lt;a class=&quot;zola-anchor&quot; href=&quot;#animations&quot; aria-label=&quot;Anchor link for: animations&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;m still unsure how to &lt;em&gt;easily&lt;&#x2F;em&gt; add animations. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;react-component&#x2F;animate&quot;&gt;animate&lt;&#x2F;a&gt; looks like a good
candidate though.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;a class=&quot;zola-anchor&quot; href=&quot;#tl-dr&quot; aria-label=&quot;Anchor link for: tl-dr&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;My starter &lt;code&gt;package.json&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;frontend&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;version&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;1.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;main&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;index.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;scripts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;clean&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;rimraf -- dist&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;dev&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;webpack-dev-server&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;build&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;webpack&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;storybook&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;start-storybook -p 6006&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;build-storybook&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;build-storybook&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;devDependencies&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;@kadira&#x2F;react-storybook-decorator-centered&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^1.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;@kadira&#x2F;storybook&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.21.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;@types&#x2F;classnames&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^0.0.32&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;@types&#x2F;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^15.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;@types&#x2F;react-dom&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^0.14.20&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;autoprefixer&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^6.6.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;css-loader&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^0.26.1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;html-webpack-plugin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.24.1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;node-sass&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^4.1.1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;rimraf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.5.4&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;sass-loader&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^4.1.1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;ts-loader&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;tslint&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^4.2.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;tslint-loader&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^3.3.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;typescript&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.1.4&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;webpack&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;2.2.0-rc.4&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;webpack-dev-server&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;2.2.0-rc.0&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;dependencies&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;classnames&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^2.2.5&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^3.0.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx-react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^4.1.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^15.4.1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react-dom&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;^15.4.1&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Releasing Tera 0.6 and thoughts on a validation crate</title>
          <pubDate>Mon, 26 Dec 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-tera-0-6/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-tera-0-6/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-tera-0-6/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt; 0.6 has been released on &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;crates.io&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;I will let the changelog speaks for itself:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;breaking-changes&quot;&gt;BREAKING CHANGES&lt;a class=&quot;zola-anchor&quot; href=&quot;#breaking-changes&quot; aria-label=&quot;Anchor link for: breaking-changes&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;not&lt;&#x2F;code&gt; is now a Tera keyword&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;others&quot;&gt;Others&lt;a class=&quot;zola-anchor&quot; href=&quot;#others&quot; aria-label=&quot;Anchor link for: others&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;Added &lt;code&gt;#![deny(missing_docs)]&lt;&#x2F;code&gt; to the crate&lt;&#x2F;li&gt;
&lt;li&gt;Added &lt;code&gt;Tera::one_off&lt;&#x2F;code&gt; to parse and render a single template&lt;&#x2F;li&gt;
&lt;li&gt;Added &lt;code&gt;not&lt;&#x2F;code&gt; operator in conditions to mean falsiness (equivalent to &lt;code&gt;!&lt;&#x2F;code&gt; in Rust)&lt;&#x2F;li&gt;
&lt;li&gt;Remove specific error message when using &lt;code&gt;||&lt;&#x2F;code&gt; or &lt;code&gt;&amp;amp;&amp;amp;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Improved performances for parsing and rendering (~5-20%)&lt;&#x2F;li&gt;
&lt;li&gt;Added &lt;code&gt;precision&lt;&#x2F;code&gt; arg to &lt;code&gt;round&lt;&#x2F;code&gt; filter&lt;&#x2F;li&gt;
&lt;li&gt;Added &lt;code&gt;date&lt;&#x2F;code&gt; filter to format a timestamp to a date(time) string&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Nothing too exciting in that release except finally handling &lt;code&gt;not&lt;&#x2F;code&gt; in expressions.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t foresee big changes in the API coming soon anymore as we approach feature completeness from my point of view.&lt;&#x2F;p&gt;
&lt;p&gt;The 2 main things still lacking are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;custom tags&lt;&#x2F;strong&gt;: we would need to expose the renderer or the AST somehow&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;i18n&lt;&#x2F;strong&gt;: no mature i18n libraries in Rust as far as I&#x27;m aware&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I am not using any of those in my own projects but anyone wanting to discuss them is welcome.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;validation-crate&quot;&gt;Validation crate&lt;a class=&quot;zola-anchor&quot; href=&quot;#validation-crate&quot; aria-label=&quot;Anchor link for: validation-crate&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been working through the &lt;code&gt;requirements.txt&lt;&#x2F;code&gt; of Proppy to decide what crate to work on in Rust.&lt;&#x2F;p&gt;
&lt;p&gt;The next one will probably be a crate similar to the &lt;a href=&quot;https:&#x2F;&#x2F;marshmallow.readthedocs.io&#x2F;en&#x2F;latest&#x2F;index.html&quot;&gt;marshmallow&lt;&#x2F;a&gt; library.
Since Serde can handle the serialization&#x2F;deserialization aspect already, it will focus only on actual validation with an API looking like
that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug, Deserialize, Validate)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;SignupData &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;(email)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;(url)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;site&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;(custom&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;validate_unique_username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;(min&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;2))]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    #[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;validate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;(min&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;8, max&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;255), custom&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;validate_common_passwords&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A non-goal of that crate will be error messages as it&#x27;s always a pain to handle. Errors will be referenced by a keyword
and it will be up to the frontend or the backend to write error messages in the correct format for that application.&lt;&#x2F;p&gt;
&lt;p&gt;The first step is figuring out how macros 1.1 work but once it&#x27;s done, it seems like it will be a fairly small crate.&lt;&#x2F;p&gt;
&lt;p&gt;See you in 2017!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Handling errors in Rust</title>
          <pubDate>Thu, 15 Dec 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/handling-errors-in-rust/</link>
          <guid>https://www.vincentprouillet.com/blog/handling-errors-in-rust/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/handling-errors-in-rust/">&lt;p&gt;Error handling in Rust is pretty straightforward.&lt;&#x2F;p&gt;
&lt;p&gt;The standard library comes with the &lt;code&gt;Result&lt;&#x2F;code&gt; type which has the following definition:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;must_use&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;T, E&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(T),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(E),
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In short, &lt;code&gt;Result&lt;&#x2F;code&gt; can either be OK with a value &lt;code&gt;T&lt;&#x2F;code&gt; or be an error with value &lt;code&gt;E&lt;&#x2F;code&gt;.
The &lt;code&gt;#[must_use]&lt;&#x2F;code&gt; annotation means that the compiler will warn you if you ignore a &lt;code&gt;Result&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;No more forgetting to catch that one exception or a &lt;code&gt;if err != nil { return nil, err }&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;handling-result&quot;&gt;Handling Result&lt;a class=&quot;zola-anchor&quot; href=&quot;#handling-result&quot; aria-label=&quot;Anchor link for: handling-result&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s have a look a small program to see the various way of creating and handling errors in Rust.
Follow this &lt;a href=&quot;https:&#x2F;&#x2F;is.gd&#x2F;ZhThdZ&quot;&gt;playground link&lt;&#x2F;a&gt; to run and play with the examples below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::fmt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::io;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::error::Error;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Our set of errors for that program
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;enum &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MyErrors &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    BadMood,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Badly implemented IO error
&lt;&#x2F;span&gt;&lt;span&gt;    FileFailure(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Correctly implemented IO error
&lt;&#x2F;span&gt;&lt;span&gt;    FileFailure2(io::Error),
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Impl display so we can have nice strings to print
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;fmt::Display &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MyErrors &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span&gt;fmt::Formatter) -&amp;gt; fmt::Result {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::FileFailure(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; err) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;write!&lt;&#x2F;span&gt;&lt;span&gt;(f, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;File creation failed: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{:?}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, err),
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::FileFailure2(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; err) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;write!&lt;&#x2F;span&gt;&lt;span&gt;(f, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;File creation failed: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{:?}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, err),
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::BadMood &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;write!&lt;&#x2F;span&gt;&lt;span&gt;(f, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Nothing wrong, just wanted to error.&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span&gt;Error &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MyErrors &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;str &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::BadMood &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;bad mood&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::FileFailure(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; err) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;file failure&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::FileFailure2(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; err) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;file failure&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;cause&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;Error&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match *&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::FileFailure(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the cause is the io::error
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::FileFailure2(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; err) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(err),
&lt;&#x2F;span&gt;&lt;span&gt;            MyErrors::BadMood &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;From&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;io::Error&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MyErrors &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;: io::Error) -&amp;gt; MyErrors {
&lt;&#x2F;span&gt;&lt;span&gt;        MyErrors::FileFailure2(err)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;will_succeed&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;, MyErrors&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;will_fail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;filename&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;(), MyErrors&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(MyErrors::FileFailure(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Failed to created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, filename)))
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;(), MyErrors&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;will_fail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;index.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we don&amp;#39;t care about the result value, it&amp;#39;s an empty tuple
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(());
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(e) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Error while creating a file:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;\n {}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, e);
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Equivalent to match above
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(e) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;will_fail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;index.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Error while creating a file:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;\n {}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, e);
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(());
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; method 1 to propagate errors: try! macro
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; did_succeed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;will_succeed&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; method 2 to propagate errors: question mark operator
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; did_succeed2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;will_succeed&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;About half of the lines are about defining errors, we will see how to reduce that boilerplate in the following section.
If you want to read more on that, the &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;error-handling.html&quot;&gt;error handling section&lt;&#x2F;a&gt; in the Rust book is very good.&lt;&#x2F;p&gt;
&lt;p&gt;The interesting part in that code is the body of the &lt;code&gt;do_something&lt;&#x2F;code&gt; function which showcases the various ways of
handling &lt;code&gt;Result&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can be in 2 situations when handling errors:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;you want to handle them immediately&lt;&#x2F;li&gt;
&lt;li&gt;you want to do an early return and pass them back to the caller&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &lt;code&gt;match&lt;&#x2F;code&gt; and &lt;code&gt;if let&lt;&#x2F;code&gt; constructs are equivalent in this case and  used if you are in the first situation.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;try!&lt;&#x2F;code&gt; and question mark operator are also equivalent: they return the error if there is one and unpacks the &lt;code&gt;Ok&lt;&#x2F;code&gt; value
otherwise.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;?&lt;&#x2F;code&gt; was stabilised in Rust 1.13 (released about one month before this post) and is somewhat controversial
as some think that error handling is hidden when using it.&lt;&#x2F;p&gt;
&lt;p&gt;I like the &lt;code&gt;?&lt;&#x2F;code&gt; operator myself since I think it makes the code neater but I let you be the judge:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something_else&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;finish&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; or cleaner
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; b &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(a.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something_else&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try!&lt;&#x2F;span&gt;&lt;span&gt;(b.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;finish&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;do_something_else&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;finish&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;avoiding-error-boilerplate&quot;&gt;Avoiding error boilerplate&lt;a class=&quot;zola-anchor&quot; href=&quot;#avoiding-error-boilerplate&quot; aria-label=&quot;Anchor link for: avoiding-error-boilerplate&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As you saw from the example above, defining your own errors is very verbose.&lt;&#x2F;p&gt;
&lt;p&gt;After experimenting on my own at first, I found the &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;quick-error&quot;&gt;quick-error&lt;&#x2F;a&gt; crate which
makes creating your own error and extending built-in ones like the &lt;code&gt;io::Error&lt;&#x2F;code&gt; in the previous section a breeze.
This was my go-to crate for error handling, until reading &lt;a href=&quot;http:&#x2F;&#x2F;brson.github.io&#x2F;2016&#x2F;11&#x2F;30&#x2F;starting-with-error-chain&quot;&gt;this article&lt;&#x2F;a&gt;
about &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;error-chain&quot;&gt;error-chain&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;error-chain&lt;&#x2F;code&gt; builds on &lt;code&gt;quick-error&lt;&#x2F;code&gt; and makes it even more painless.&lt;&#x2F;p&gt;
&lt;p&gt;I have switched &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt; 0.5 to use &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;error-chain&quot;&gt;error-chain&lt;&#x2F;a&gt; and am very
happy about the end result.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;errors.rs&lt;&#x2F;code&gt; file in Tera went from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;blob&#x2F;3471df41ab454c60a85ec271a945f7123705e49a&#x2F;src&#x2F;errors.rs&quot;&gt;~80 lines&lt;&#x2F;a&gt;
and lots of custom errors to:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;error_chain! &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    errors {}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is nothing you might say. And you would be somewhat correct!
Using the &lt;code&gt;error_chain!&lt;&#x2F;code&gt; macro gives me &lt;code&gt;Result&lt;&#x2F;code&gt;, &lt;code&gt;ResultExt&lt;&#x2F;code&gt; (a trait), &lt;code&gt;ErrorKind&lt;&#x2F;code&gt; and &lt;code&gt;Error&lt;&#x2F;code&gt; but I didn&#x27;t define any
custom errors myself.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s obviously not always empty though, here&#x27;s the &lt;code&gt;errors.rs&lt;&#x2F;code&gt; of a static site engine using Tera:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt; tera;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;error_chain! &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    links {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Links Tera errors to that crate
&lt;&#x2F;span&gt;&lt;span&gt;        Tera(tera::Error, tera::ErrorKind);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    foreign_links {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Link with errors not defined with error-chain
&lt;&#x2F;span&gt;&lt;span&gt;        Io(::std::io::Error);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    errors {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; I&amp;#39;m using this one lots of time so creating it there to keep it DRY
&lt;&#x2F;span&gt;&lt;span&gt;        InvalidConfig {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;invalid config&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;display&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;The config.toml is invalid or is using the wrong type for an argument&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The article linked previously made me realise that you need custom errors in 2 occasions: the user of the library will pattern match on them or you want
to avoid repeating yourself like the &lt;code&gt;InvalidConfig&lt;&#x2F;code&gt; above.&lt;&#x2F;p&gt;
&lt;p&gt;In Tera case, I was able to replace all the errors with &lt;code&gt;bail!&lt;&#x2F;code&gt; macro that comes with &lt;code&gt;error-chain&lt;&#x2F;code&gt;.
This is a very simple macro that works similarly to &lt;code&gt;println!&lt;&#x2F;code&gt; except it returns an error with the text given:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; something_is_wrong {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;bail!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Something wrong happened while doing {:?}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, action);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; expands to
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; something_is_wrong {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Something wrong happened while doing &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{:?}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, action).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It doesn&#x27;t look like much but using stringly typed errors saves a lot of time and makes you write better errors
at the same time as you can write very specific errors without any boilerplate.&lt;&#x2F;p&gt;
&lt;p&gt;But the killer feature of &lt;code&gt;error-chain&lt;&#x2F;code&gt; is to chain errors, as its name implies.
You often want to add context to errors and chaining allows just that.&lt;&#x2F;p&gt;
&lt;p&gt;The easiest example is a function to open a file: Rust doesn&#x27;t include the filename in the error but you usually want it if
you are going to display it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;errors::ResultExt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;File::open(path)
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;chain_err&lt;&#x2F;span&gt;&lt;span&gt;(|| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;format!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Failed to open &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;.&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, path))&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;read_to_string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; content)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;chain_err&lt;&#x2F;code&gt; is coming from the &lt;code&gt;ResultExt&lt;&#x2F;code&gt; and is where the magic happens. If an error in &lt;code&gt;File::open&lt;&#x2F;code&gt; happens,
it will create a new error, storing the one caused by &lt;code&gt;File::open&lt;&#x2F;code&gt; as its cause. Errors can be chained multiple times, allowing you
to annotate errors at several levels and giving detailed error messages.&lt;&#x2F;p&gt;
&lt;p&gt;Printing all chained errors is as simple as the code below:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Error: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, e);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; e &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; e.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;skip&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Reason: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, e);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;m liking this approach quite a lot and will be using it for all my projects for the foreseeable future!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Releasing Tera 0.4 and state of my other crates</title>
          <pubDate>Fri, 02 Dec 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/releasing-tera-0-4/</link>
          <guid>https://www.vincentprouillet.com/blog/releasing-tera-0-4/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/releasing-tera-0-4/">&lt;p&gt;I&#x27;m happy to announce &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt; 0.4 has been released on &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;crates.io&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Tera is a template engine based on &lt;a href=&quot;http:&#x2F;&#x2F;jinja.pocoo.org&#x2F;docs&#x2F;dev&#x2F;&quot;&gt;Jinja2&lt;&#x2F;a&gt; and the &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;1.10&#x2F;ref&#x2F;templates&#x2F;language&#x2F;&quot;&gt;Django template language&lt;&#x2F;a&gt;.
I say based as it&#x27;s not a 1:1 port and has some minor differences with them but is close enough that you should be able
to use the same syntax highlighting.&lt;&#x2F;p&gt;
&lt;p&gt;This version introduces the last big features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;docs&#x2F;#macros&quot;&gt;Macros&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;docs&#x2F;#inheritance&quot;&gt;Multiple level extend, super() and nested blocks&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tera.netlify.com&#x2F;docs&#x2F;#auto-escaping&quot;&gt;Autoescape&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It also adds the &lt;code&gt;filesizeformat&lt;&#x2F;code&gt; filter thanks to the &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;humansize&quot;&gt;humansize crate&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Some bits are experimental (named endblock&#x2F;endmacro, kwargs only) and subject to changes depending on feedback.&lt;&#x2F;p&gt;
&lt;p&gt;There are probably features that others deem essential (please &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&quot;&gt;create an issue&lt;&#x2F;a&gt; if you can think of one missing!) but for my own use case it seems feature complete, apart from
the missing &lt;code&gt;not&lt;&#x2F;code&gt; operator in conditions. Custom tags would be nice too.&lt;&#x2F;p&gt;
&lt;p&gt;The focus for the next version will be on:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;fixing the inevitable bugs&lt;&#x2F;li&gt;
&lt;li&gt;add the &lt;code&gt;not&lt;&#x2F;code&gt; operator&lt;&#x2F;li&gt;
&lt;li&gt;improving documentation: a website and more examples&lt;&#x2F;li&gt;
&lt;li&gt;improving error handling and reporting (maybe &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;brson&#x2F;error-chain&quot;&gt;error-chain&lt;&#x2F;a&gt; could help there?)&lt;&#x2F;li&gt;
&lt;li&gt;optimizations and code cleanup&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As you can see, no new big features planned, it will be mostly polishing.&lt;&#x2F;p&gt;
&lt;p&gt;To ensure Tera is easy to use, I&#x27;m thinking of making a static site engine using it but that will depend on how much free time I have.
I&#x27;m currently using &lt;a href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt; for all my sites but the Golang template engine is godawful.&lt;&#x2F;p&gt;
&lt;p&gt;Making the templates compile to Rust functions is still not planned in the near future.
Check out &lt;a href=&quot;https:&#x2F;&#x2F;maud.lambda.xyz&#x2F;&quot;&gt;Maud&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;horrorshow&#x2F;0.6.1&#x2F;horrorshow&#x2F;&quot;&gt;horrorshow&lt;&#x2F;a&gt; if you want type-safe HTML.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to help you have 2 options: use it and report bugs and&#x2F;or look at the issues tagged for the 0.5 milestone on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;issues&quot;&gt;github&lt;&#x2F;a&gt;.
The code is fairly clean, commented and has lots of tests so it should be easy to jump in.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SergioBenitez&quot;&gt;SergioBenitez&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;yonran&quot;&gt;yonran&lt;&#x2F;a&gt; for the help!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-crates&quot;&gt;Other crates&lt;a class=&quot;zola-anchor&quot; href=&quot;#other-crates&quot; aria-label=&quot;Anchor link for: other-crates&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;bcrypt-0-1-2&quot;&gt;bcrypt (0.1.2)&lt;a class=&quot;zola-anchor&quot; href=&quot;#bcrypt-0-1-2&quot; aria-label=&quot;Anchor link for: bcrypt-0-1-2&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This one has an explicit name and is stable.
I was thinking of extracting the bcrypt specific code from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DaGenix&#x2F;rust-crypto&quot;&gt;rust-crypto&lt;&#x2F;a&gt;
but someone already started on that: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RustCrypto&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;RustCrypto&lt;&#x2F;a&gt; so I&#x27;ll just wait.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dbmigrate-0-2-7&quot;&gt;dbmigrate (0.2.7)&lt;a class=&quot;zola-anchor&quot; href=&quot;#dbmigrate-0-2-7&quot; aria-label=&quot;Anchor link for: dbmigrate-0-2-7&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A CLI tool to general and manage SQL migrations for MySQL, SQLite and Postgres.
It is stable and I don&#x27;t foresee the API or the migration file format changing so I will probably promote it to 1.0 soon.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;jsonwebtoken-1-1-5&quot;&gt;jsonwebtoken (1.1.5)&lt;a class=&quot;zola-anchor&quot; href=&quot;#jsonwebtoken-1-1-5&quot; aria-label=&quot;Anchor link for: jsonwebtoken-1-1-5&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Strongly typed JWT library. I like the current API but a few more features are planned for 2.0:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;moving to Serde from rustc-serialize&lt;&#x2F;li&gt;
&lt;li&gt;implementing the missing algorithms&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Issues are created on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;rust-jwt&#x2F;issues&quot;&gt;github repo&lt;&#x2F;a&gt; so feel free to take one of these before I get to them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;vat-0-1-0&quot;&gt;vat (0.1.0)&lt;a class=&quot;zola-anchor&quot; href=&quot;#vat-0-1-0&quot; aria-label=&quot;Anchor link for: vat-0-1-0&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Validate EU VAT number and VAT rates for each country. Still not entirely sure why I made that crate but I think I&#x27;ll try switching it
to &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;reqwest&quot;&gt;reqwest&lt;&#x2F;a&gt; when I have time.&lt;&#x2F;p&gt;
&lt;p&gt;I also never got around handling errors from the VIES API since it was always working when I was looking at it and they don&#x27;t (AFAIK) detail
their error messages anywhere.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A MobX introduction and case study</title>
          <pubDate>Tue, 01 Nov 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/a-mobx-introduction/</link>
          <guid>https://www.vincentprouillet.com/blog/a-mobx-introduction/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/a-mobx-introduction/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mobxjs.github.io&#x2F;mobx&#x2F;&quot;&gt;MobX&lt;&#x2F;a&gt; (previously mobservable) is a state management library for JavaScript frontend application. This article introduces it with examples as well as showing how it works in a real app with TypeScript. It is based on a talk I gave at the Osaka Web designers and developers meetup recently.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-mobx-and-why-should-i-look-into-it&quot;&gt;What is MobX and why should I look into it&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-mobx-and-why-should-i-look-into-it&quot; aria-label=&quot;Anchor link for: what-is-mobx-and-why-should-i-look-into-it&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;mobx&quot;&gt;MobX?&lt;a class=&quot;zola-anchor&quot; href=&quot;#mobx&quot; aria-label=&quot;Anchor link for: mobx&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s start by explaining what is MobX and how it works. It&#x27;s presented as using Transparent Functional Reactive Programming.&lt;&#x2F;p&gt;
&lt;p&gt;Now, FRP is a very controversial term as everyone seems to have their own definition so let&#x27;s forget about that and look at a illustration from the MobX docs (click on it to open in a new tab and get full size or open &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;mobx-flow.png&quot;&gt;this link&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;mobx-flow.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;mobx-flow.png&quot; alt=&quot;MobX flow&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;In short, actions modify the state, which triggers reactions. Part of the state can be derived automatically, such as the number of tasks left to do in a TODO list to take the example of the picture above.
What sets MobX apart from other Observable implementations is the transparent part. Reactions observe which observables you are using and subscribe to them automatically, without you having to explicitely subscribe to those.&lt;&#x2F;p&gt;
&lt;p&gt;Before we continue, a few more things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;while some of the examples use React, MobX is not tied to any particular framework&lt;&#x2F;li&gt;
&lt;li&gt;it is written in TypeScript so you get type definitions out of the box if you are using TypeScript&lt;&#x2F;li&gt;
&lt;li&gt;the learning curve is very small and you will be able to start an app by the end of that article&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;why-would-i-use-it-instead-of-redux-immutablejs-or-library-x&quot;&gt;Why would I use it instead of Redux&#x2F;ImmutableJS or library X?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-would-i-use-it-instead-of-redux-immutablejs-or-library-x&quot; aria-label=&quot;Anchor link for: why-would-i-use-it-instead-of-redux-immutablejs-or-library-x&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This part is written from my experience using both Redux&#x2F;ImmutableJS and MobX with React in Proppy, our app to write proposals for freelancers and small agencies&#x2F;companies. Its frontend is written in TypeScript, which was one of a big reason to moving to MobX as we will see in a bit.&lt;&#x2F;p&gt;
&lt;p&gt;Our issues with Redux&#x2F;ImmutableJS were twofold.&lt;&#x2F;p&gt;
&lt;p&gt;The first issue was that it is very verbose. You need to write an action, the reducer function, a selector and then connect the actions and the data on the React components.&lt;&#x2F;p&gt;
&lt;p&gt;The second issue only occurs if you are using TypeScript like us. You also need to add the types of actions and data at each step and sometimes even repeat them if you are passing down actions to dumb components. We were also using ImmutableJS records with maps in them which meant the code was very often stringly typed. For example, getting a value using &lt;code&gt;.getIn([&quot;blocks&quot;, id, &quot;data&quot;, &quot;value&quot;])&lt;&#x2F;code&gt; was common and there was no typecheck whatsoever.&lt;&#x2F;p&gt;
&lt;p&gt;With MobX we only have to mention the types and, as we will see when showing examples of Proppy, get full typechecking as we are only dealing with plain JavaScript objects.&lt;&#x2F;p&gt;
&lt;p&gt;Note that using MobX means you have to give up immutability, which could be a showstopper for you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mobx-by-example&quot;&gt;MobX by example&lt;a class=&quot;zola-anchor&quot; href=&quot;#mobx-by-example&quot; aria-label=&quot;Anchor link for: mobx-by-example&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This section is a brief introduction to MobX. If you have already used it, you can safely skip to the next section to see some patterns&#x2F;snippets from a live application.&lt;&#x2F;p&gt;
&lt;p&gt;Note that I will use ES6 syntax and decorators throughout the examples but it works in plain ES5 as well.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;observing-arrays-and-map-changes-with-autorun&quot;&gt;Observing arrays and map changes with autorun&lt;a class=&quot;zola-anchor&quot; href=&quot;#observing-arrays-and-map-changes-with-autorun&quot; aria-label=&quot;Anchor link for: observing-arrays-and-map-changes-with-autorun&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s start with observing arrays and maps, as it constitutes a big chunk of what we would want
to observe.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;asMap&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;autorun &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; becomes an ObservableArray (same API as normal array)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numbers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;observable&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; becomes an ObservableMap (same API as Map)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;users &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;observable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;asMap&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;bob&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;bob&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}));
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To show the changes to observable values, we will use the &lt;code&gt;autorun&lt;&#x2F;code&gt; function provided by the mobx package. This function takes a function as argument and call it once to detect which observables are being used in it and subscribe to them.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;autorun&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;autorun!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numbers&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;toJS&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;toJS&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; initial run: prints autorun! [1,2,3] {&amp;quot;bob&amp;quot;: &amp;quot;bob&amp;quot;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numbers&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; prints autorun! [1,2,3,4] {&amp;quot;bob&amp;quot;: &amp;quot;bob&amp;quot;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;users&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;jane&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;jane&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; prints autorun! [1,2,3,4] {&amp;quot;bob&amp;quot;: &amp;quot;bob&amp;quot;, &amp;quot;jane&amp;quot;: &amp;quot;jane&amp;quot;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We will show in the next section that &lt;code&gt;autorun&lt;&#x2F;code&gt; doesn&#x27;t actually run everytime any observable is modified like the example above might indicate but only when one of those used in the function are modified.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;observing-class-properties-and-transactions&quot;&gt;Observing class properties and transactions&lt;a class=&quot;zola-anchor&quot; href=&quot;#observing-class-properties-and-transactions&quot; aria-label=&quot;Anchor link for: observing-class-properties-and-transactions&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The other thing that we typically want to observe are class properties.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;autorun &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Meetup &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable name&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable numberPeople&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Meetup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Osaka Web designers and developers&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;autorun&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot; people&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; prints &amp;quot;1 people&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In that example, both &lt;code&gt;name&lt;&#x2F;code&gt; and &lt;code&gt;numberPeople&lt;&#x2F;code&gt; are observable properties of the &lt;code&gt;thisMeetup&lt;&#x2F;code&gt; object but only &lt;code&gt;numberPeople&lt;&#x2F;code&gt; is used in the &lt;code&gt;autorun&lt;&#x2F;code&gt; function. Therefore, the following snippet will not print anything:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Hello&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; doesn&amp;#39;t print anything
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Remember the T part of TFRP? MobX automatically figured out which observable to subscribe to and has done so transparently.&lt;&#x2F;p&gt;
&lt;p&gt;Reactions are synchronous so you might wonder how to avoid &quot;useless&quot; reactions, e.g. when setting multiple observables at once such as when receiving a response from a AJAX request. &lt;code&gt;transaction&lt;&#x2F;code&gt; solves that issue by not triggering reactions until the end of the function.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;transaction&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;thisMeetup&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numberPeople &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; print &amp;quot;4 people&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the example above, &lt;code&gt;autorun&lt;&#x2F;code&gt; only runs once: with &lt;code&gt;numberPeople&lt;&#x2F;code&gt; equal to 4, not with the intermediate 2 and 3.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;computed-values&quot;&gt;Computed values&lt;a class=&quot;zola-anchor&quot; href=&quot;#computed-values&quot; aria-label=&quot;Anchor link for: computed-values&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In some cases, data can be derived directly from the state. To take a simplified example from Proppy, we are going to look at a row in a cost table: we have quantity and unit price and can therefore automatically calculate the total.
&lt;code&gt;computed&lt;&#x2F;code&gt; is lazy (won&#x27;t be evaluated unless needed in a reaction) and it is memoized (if the observables used inside didn&#x27;t change, it doesn&#x27;t need to re-run and can return the current value).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;TableRow &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable price &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; a getter
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;computed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;get &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;TableRow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; prints 0 (since 10x0=0)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Only computed on the call
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; prints 100
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Not recomputed if the observables used didn&amp;#39;t change
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; prints 100
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that &lt;code&gt;@computed&lt;&#x2F;code&gt; can only be used on &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Functions&#x2F;get&quot;&gt;getter&lt;&#x2F;a&gt; in a class.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;actions&quot;&gt;Actions&lt;a class=&quot;zola-anchor&quot; href=&quot;#actions&quot; aria-label=&quot;Anchor link for: actions&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Actions are functions that modify the state.
While, as shown in the previous examples, we can simply modify the object directly, it is not the recommended way to do so.&lt;&#x2F;p&gt;
&lt;p&gt;The recommended way is to wrap any actions that modify the state with the &lt;code&gt;action&lt;&#x2F;code&gt; function or decorator. This will accomplish a few things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;wrap the function in a transaction automatically&lt;&#x2F;li&gt;
&lt;li&gt;provide debugging info for that state change that can be displayed in dev tools&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can force your app to only modify state through actions by using &lt;code&gt;useStrict(true)&lt;&#x2F;code&gt; in your app.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;useStrict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;TableRow &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; a getter
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;computed &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;get &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;price &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;setQuantity&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;TableRow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;setQuantity&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Only computed on the call
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Not recomputed if the observables used didn&amp;#39;t change
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;total&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Use `useStrict` to force state to only be modified in `action` fns
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quantity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; will error
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We don&#x27;t use the strict mode ourselves&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mobx-in-real-life&quot;&gt;MobX in real life&lt;a class=&quot;zola-anchor&quot; href=&quot;#mobx-in-real-life&quot; aria-label=&quot;Anchor link for: mobx-in-real-life&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that you know the basics of MobX, it&#x27;s time to see how it looks in production.
It&#x27;s currently the backbone of our frontend app in Proppy so we will use examples from it.&lt;&#x2F;p&gt;
&lt;p&gt;If you are doing SSR, be aware that the way we handle stores shown below will not work for you.&lt;&#x2F;p&gt;
&lt;p&gt;Converting Proppy to use MobX took about 4 days, numerous long-standing issues were fixed along the way and the end result was a deletion of around 1000 to 2000 lines (estimate as we also moved to use npm @types for typings in that branch).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;stores&quot;&gt;Stores&lt;a class=&quot;zola-anchor&quot; href=&quot;#stores&quot; aria-label=&quot;Anchor link for: stores&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Our stores are simple singleton classes that have observable properties.
Here&#x27;s a shorter version of one of our stores, dealing with clients for proposals.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;transaction&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ObservableMap &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; a class definition with types
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Client &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;models&#x2F;Client&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fetchling &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;..&#x2F;utils&#x2F;fetchling&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uiStore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;UIStore&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;ClientStore &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable clients&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;ObservableMap&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;getClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clients&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchAll&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchling&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&#x2F;clients&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;transaction&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;setClients&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clients&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;deleteClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clients&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchling&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`&#x2F;clients&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#acb3c2;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clients&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;delete&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uiStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;notify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`Client deleted`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;catch&lt;&#x2F;span&gt;&lt;span&gt;(() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uiStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;notify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`Client ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#acb3c2;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;} could not be deleted. Please try again later`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;updateClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchling&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`&#x2F;clients&#x2F;${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#acb3c2;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;put&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clients&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientId&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uiStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;notify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`Client updated`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;      })
&lt;&#x2F;span&gt;&lt;span&gt;      .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;catch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uiStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;notify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`Client could not be updated. Please try again later`&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;      });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;ClientStore&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export default &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Quite simple stuff.
You can see we are calling another store from our store. That would be a big no-no in Redux and we were using a middleware to solve that previously.
It feels much nicer now: we have a full view of what&#x27;s happening and it&#x27;s very easy to see if something is missing (which was actually the case for some ajax requests).&lt;&#x2F;p&gt;
&lt;p&gt;This example doesn&#x27;t really show it but every data stored in a store is typed and trying to access &lt;code&gt;client.random&lt;&#x2F;code&gt; would be a compiler error.
It really shines for complex objects like cost tables that were previously a simple map with no schema and are now fully typed objects with functions of their own. Thanks to that, we moved things such as calculating totals to the cost table class rather than a React component like before.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;components&quot;&gt;Components&lt;a class=&quot;zola-anchor&quot; href=&quot;#components&quot; aria-label=&quot;Anchor link for: components&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We&#x27;re using the &lt;code&gt;mobx-react&lt;&#x2F;code&gt; package that gives us the &lt;code&gt;observer&lt;&#x2F;code&gt; decorator that we put on top of a component.
Doing so gives us the same thing as the &lt;code&gt;autorun&lt;&#x2F;code&gt; in the MobX by example section: when an observable used in the &lt;code&gt;render&lt;&#x2F;code&gt; function changes, the component will be re-rendered.
&lt;code&gt;@observer&lt;&#x2F;code&gt; also comes with an optimized &lt;code&gt;shouldComponentUpdate&lt;&#x2F;code&gt; that works very well in our experience: the only &lt;code&gt;shouldComponentUpdate&lt;&#x2F;code&gt; left in our code are the ones impossible to not handle manually due to &lt;code&gt;contenteditable&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Since we don&#x27;t do server side rendering, we can directly import a store and call its function directly. Here&#x27;s a slightly modified code sample for the Settings&amp;gt;Clients page:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;tsx&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-tsx &quot;&gt;&lt;code class=&quot;language-tsx&quot; data-lang=&quot;tsx&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;React  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observer &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx-react&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable &lt;&#x2F;span&gt;&lt;span&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;mobx&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;companyStore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;..&#x2F;..&#x2F;stores&#x2F;CompanyStore&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;..&#x2F;..&#x2F;stores&#x2F;ClientStore&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; ... some component import
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observer
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Clients &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;extends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;React&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;Component&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;{}, {}&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; not used, only here for the sake of example
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observable clientAdded&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;boolean &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Our async loaded component will call that function and wait until
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the promises complete to render that component
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchData&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Promise&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;companyStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchUs&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;fetchAll&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;    ]);
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;renderClients&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clients&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;values&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;client&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;}&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;span onClick&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.deleteClient(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;)} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;icon-delete&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;InlineInput
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;onEnter&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.updateClient(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;)} &#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;      );
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;renderForm&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;InAppForm
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;inline&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;resetOnSuccess
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;submitText&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Add client&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;onSubmit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;{(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;clientStore&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.createClient(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;)} &#x2F;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;SettingsContainer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;clients&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;Manage {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;your&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;} clients&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;h2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;          {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.renderForm()}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div className&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;clients-list&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;            {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;.renderClients()}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;          &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;SettingsContainer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    );
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No ceremony needed, everything works out of the box as if I was just dealing with plain JavaScript objects. All the store functions and data are typed too. Trying to call &lt;code&gt;clientStore.deleteClient(&quot;1&quot;)&lt;&#x2F;code&gt; for example would be a compilation error and I only had to write the type once: in the store as shown before.&lt;&#x2F;p&gt;
&lt;p&gt;Another aspect not mentioned before is that you can also use &lt;code&gt;observable&lt;&#x2F;code&gt; to remove the need for React state.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than having a state and updating it with &lt;code&gt;this.setState&lt;&#x2F;code&gt;, you can just update your variable directly and it will re-render the component if that property is used in the render method.
To use the previous example, if I wanted to display a banner after adding a client in that component I could set &lt;code&gt;this.client = true&lt;&#x2F;code&gt; and render something different when &lt;code&gt;this.client&lt;&#x2F;code&gt; is true. No more &lt;code&gt;this.setState({..} as any)&lt;&#x2F;code&gt; to satisfy the TypeScript compiler.
Another neat thing about using MobX for state is that changes will be logged if you are using dev tools.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dev-tools&quot;&gt;Dev tools&lt;a class=&quot;zola-anchor&quot; href=&quot;#dev-tools&quot; aria-label=&quot;Anchor link for: dev-tools&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;For React, &lt;code&gt;mobx-react-devtools&lt;&#x2F;code&gt; is available and is pretty great. See the gif below from its repo to have an overview of the 3 features.&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;devtools.gif&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;a-mobx-introduction&#x2F;devtools.gif&quot; alt=&quot;React MobX devtools&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I have mostly used the logging to inspect action and state changes while debugging and to spot erroneous updates and the re-rendering highlighter to figure out if some of our components were re-rendering too often.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;useful-links&quot;&gt;Useful links&lt;a class=&quot;zola-anchor&quot; href=&quot;#useful-links&quot; aria-label=&quot;Anchor link for: useful-links&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mobxjs.github.io&#x2F;mobx&#x2F;&quot;&gt;Official docs&lt;&#x2F;a&gt; and in particular &lt;a href=&quot;https:&#x2F;&#x2F;mobxjs.github.io&#x2F;mobx&#x2F;best&#x2F;pitfalls.html&quot;&gt;Common pitfalls &amp;amp; best practices&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;mobxjs.github.io&#x2F;mobx&#x2F;best&#x2F;react.html&quot;&gt;What does MobX reacts to?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@mweststrate&#x2F;becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254&quot;&gt;In-depth explanation of MobX&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mobxjs&#x2F;mobx&#x2F;issues&#x2F;199&quot;&gt;Understanding MobX and when to use it&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mobxjs&#x2F;mobx&#x2F;issues&#x2F;104&quot;&gt;Example: real-life with router&#x2F;ajax etc&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Overall, we are very happy with the transition to MobX and would recommend investigating it if you are looking for a state management solution, especially if you are using TypeScript or Flow.
I&#x27;m pretty impressed by the constant work the TypeScript team has accomplished and highly recommend it as well if you are planning to stay on the JavaScript side and not go for Elm&#x2F;PureScript&#x2F;etc.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Where to incorporate if you are a EU citizen</title>
          <pubDate>Sun, 10 Jul 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/looking-at-where-to-incorporate/</link>
          <guid>https://www.vincentprouillet.com/blog/looking-at-where-to-incorporate/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/looking-at-where-to-incorporate/">&lt;p&gt;We are very lucky as EU citizens to have access to 28 (well, soon 27 apparently) countries.
To give a bit of context to this blog post, We Are Wizards is currently a company based in the UK, founded by 2 French and one German, all living in London at the moment.&lt;&#x2F;p&gt;
&lt;p&gt;While this post tries to be general, it is guided by our own requirements. There might be inaccuracies in this article so don&#x27;t take my word for it and contact companies&#x2F;government in the countries before making a decision if you decide to do so. We&#x27;ve added the tax rates so you can get a feel of the different options but they are not a deciding factor for us.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;our-requirements&quot;&gt;Our requirements&lt;a class=&quot;zola-anchor&quot; href=&quot;#our-requirements&quot; aria-label=&quot;Anchor link for: our-requirements&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned in the introduction, we are French and German and the only common language is English so being able to do all (or at least the vast majority) government&#x2F;bank interactions in English is a hard requirement.&lt;&#x2F;p&gt;
&lt;p&gt;We also need to be able to incorporate in a country without living there since we don&#x27;t want to move or, in my case, want to travel. This is especially true if you are planning to be a digital nomad.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, we want a business-friendly country with a good record on privacy and surveillance law as we run &lt;a href=&quot;https:&#x2F;&#x2F;passopolis.com&#x2F;&quot;&gt;Passopolis&lt;&#x2F;a&gt;, an open-source password manager that relies on end-to-end encryption.&lt;&#x2F;p&gt;
&lt;p&gt;To sum it up, our requirements are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;we need to be able to do things in English&lt;&#x2F;li&gt;
&lt;li&gt;we need to be able to incorporate without living there&lt;&#x2F;li&gt;
&lt;li&gt;we want its government to be privacy friendly and not authoritarian&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Your own requirements will obviously differ and some countries that we disregard in the next section might be the perfect fit for you. Or you might want to get out of the EU to not deal with &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-payments-in-europe&#x2F;&quot;&gt;VAT MOSS&lt;&#x2F;a&gt; like &lt;a href=&quot;https:&#x2F;&#x2F;blog.ghost.org&#x2F;moving-to-singapore&#x2F;&quot;&gt;Ghost&lt;&#x2F;a&gt; for example.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;countries-selection&quot;&gt;Countries selection&lt;a class=&quot;zola-anchor&quot; href=&quot;#countries-selection&quot; aria-label=&quot;Anchor link for: countries-selection&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;France, Germany, Spain, Portugal, the Netherlands and Denmark fail as I couldn&#x27;t find all the needed information on incorporation in English which means that the local language is required for all interactions.&lt;&#x2F;p&gt;
&lt;p&gt;The USA (via &lt;a href=&quot;https:&#x2F;&#x2F;stripe.com&#x2F;atlas&quot;&gt;Stripe Atlas&lt;&#x2F;a&gt;) also doesn&#x27;t make much sense from a EU citizen point of view as we have access to better choices as we will see in a moment and opens you to their patent laws.
This might be different if you are planning to raise money in the US though but I don&#x27;t know anything about that.&lt;&#x2F;p&gt;
&lt;p&gt;With the rejected countries out of the way, let&#x27;s go over the potential choices I have identified after a bit of research.&lt;&#x2F;p&gt;
&lt;p&gt;To keep the each country information DRY (Don&#x27;t Repeat Yourself), one advice common to every country including your own is to get an accountant from the start as they are not that expensive in the grand scheme of things and they make everything easier.&lt;&#x2F;p&gt;
&lt;p&gt;Do note that you can Google &quot;company formation country_X&quot; and you will get lots of companies offering services helping you with incorporation and taxes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;united-kingdom&quot;&gt;United Kingdom&lt;a class=&quot;zola-anchor&quot; href=&quot;#united-kingdom&quot; aria-label=&quot;Anchor link for: united-kingdom&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;trivia&quot;&gt;Trivia&lt;a class=&quot;zola-anchor&quot; href=&quot;#trivia&quot; aria-label=&quot;Anchor link for: trivia&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The UK is very business friendly and the biggest tech centre in Europe. You can probably go to a tech meetup every day if you wanted to and you get to meet and work with people from all over the world.&lt;&#x2F;p&gt;
&lt;p&gt;In terms of privacy laws, the UK is not doing too great with the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Investigatory_Powers_Bill&quot;&gt;IP Bill&lt;&#x2F;a&gt; it&#x27;s trying to pass.&lt;&#x2F;p&gt;
&lt;p&gt;The obvious elephant in the room is &lt;strong&gt;Brexit&lt;&#x2F;strong&gt;. No one really knows when it will actually happen and what will be the outcome so there is a great deal of uncertainty about, well, everything.
The biggest uncertainty for us is our residency status as we are not UK citizens and there is a greater than zero risk that we would not be able to stay. Getting citizenship is not an option for me for example as I&#x27;m moving out of the country soon.&lt;&#x2F;p&gt;
&lt;p&gt;Sadly enough, EU VAT handling could &lt;a href=&quot;http:&#x2F;&#x2F;www.bmmagazine.co.uk&#x2F;news&#x2F;uk-leave-eu-vat-regime-following-brexit&#x2F;&quot;&gt;become even harder than the current VAT MOSS for UK businesses&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The UK is currently the place to be in Europe if you want to get investment but it might change after Brexit.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;taxes&quot;&gt;Taxes&lt;a class=&quot;zola-anchor&quot; href=&quot;#taxes&quot; aria-label=&quot;Anchor link for: taxes&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The corporation tax is currently &lt;strong&gt;20%&lt;&#x2F;strong&gt; but there are &lt;a href=&quot;http:&#x2F;&#x2F;www.telegraph.co.uk&#x2F;business&#x2F;2016&#x2F;07&#x2F;04&#x2F;will-osbornes-plan-to-cut-corporation-tax-help-boost-the-post-br&#x2F;&quot;&gt;talks to reduce it to 17% by 2020&lt;&#x2F;a&gt; and potentially even further.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.gov.uk&#x2F;tax-on-dividends&#x2F;how-dividends-are-taxed&quot;&gt;Dividend tax&lt;&#x2F;a&gt; depends on your total income and can vary quite a bit.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-to-incorporate-open-bank-account&quot;&gt;How to incorporate&#x2F;open bank account&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-incorporate-open-bank-account&quot; aria-label=&quot;Anchor link for: how-to-incorporate-open-bank-account&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Incorporating is very simple and usually only takes a day or so. Most accountants will offer incorporation as a freebie.
You can open a bank account remotely and most banks in the UK (at least the one I&#x27;ve seen) offer a good online banking experience.&lt;&#x2F;p&gt;
&lt;p&gt;With an accountant and a tool like FreeAgent or Xero, you don&#x27;t really need to do any significant business administration work. We haven&#x27;t run into issues with HMRC (the revenue service in the UK) that you are dealing with as a company but I have heard many horror stories about it.&lt;&#x2F;p&gt;
&lt;p&gt;Virtual offices are easy to find in London so you don&#x27;t have to live in the country.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ireland&quot;&gt;Ireland&lt;a class=&quot;zola-anchor&quot; href=&quot;#ireland&quot; aria-label=&quot;Anchor link for: ireland&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;trivia-1&quot;&gt;Trivia&lt;a class=&quot;zola-anchor&quot; href=&quot;#trivia-1&quot; aria-label=&quot;Anchor link for: trivia-1&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The other obvious choice when it comes to English speaking countries in the EU. It is also famous for being the main place for big companies to do tax avoidance with a special arrangement called the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Double_Irish_arrangement&quot;&gt;Double Irish arrangement&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ireland is very business friendly, doesn&#x27;t seem to want to ban encryption any time soon and its culture is similar to the one in the UK.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, politics there seem partially driven by religion and they have actually introduced a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Blasphemy_law_in_the_Republic_of_Ireland&quot;&gt;blasphemy law&lt;&#x2F;a&gt; in 2009, which seems hard to believe.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;taxes-1&quot;&gt;Taxes&lt;a class=&quot;zola-anchor&quot; href=&quot;#taxes-1&quot; aria-label=&quot;Anchor link for: taxes-1&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The corporation tax is &lt;strong&gt;12.5%&lt;&#x2F;strong&gt; and it might get interesting if the UK and Ireland get into a lower corporation tax war. The dividend tax rate is a flat &lt;strong&gt;20%&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a &lt;strong&gt;tax exemption for the first three years&lt;&#x2F;strong&gt; if the company&#x27;s corporation tax to pay does not exceed €40,000, which corresponds to €320,000 of profit at the 12.5% tax rate. In short, unless you&#x27;re growing super fast in terms of profit, you&#x27;re not paying any corporation tax for the first three years.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-to-incorporate-open-bank-account-1&quot;&gt;How to incorporate&#x2F;open bank account&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-incorporate-open-bank-account-1&quot; aria-label=&quot;Anchor link for: how-to-incorporate-open-bank-account-1&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Incorporating in Ireland takes 2-3 days and cost €320 euros. Opening a bank account might be possible remotely but it might be easier to do a day trip to Dublin.&lt;&#x2F;p&gt;
&lt;p&gt;Virtual offices are easy to find in Dublin so you don&#x27;t have to live in the country.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;estonia&quot;&gt;Estonia&lt;a class=&quot;zola-anchor&quot; href=&quot;#estonia&quot; aria-label=&quot;Anchor link for: estonia&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;trivia-2&quot;&gt;Trivia&lt;a class=&quot;zola-anchor&quot; href=&quot;#trivia-2&quot; aria-label=&quot;Anchor link for: trivia-2&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;I&#x27;ll be the first one to say I don&#x27;t know much about Estonia. According to &lt;a href=&quot;http:&#x2F;&#x2F;www.eas.ee&#x2F;?lang=en&quot;&gt;Enterprise Estonia&lt;&#x2F;a&gt;, most of the interactions with the government and banks can be done in English. Having a local partner is useful for the more complex cases where it might be needed and, since you should get a local accountant, this shouldn&#x27;t be an issue.&lt;&#x2F;p&gt;
&lt;p&gt;The political environment seems pretty quiet and the current leading political party, the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Estonian_Reform_Party&quot;&gt;Estonia Reform Party&lt;&#x2F;a&gt; is advocating for even more liberalism when it comes to companies.&lt;&#x2F;p&gt;
&lt;p&gt;Estonia ranks 1st on the Barclays Digital Development index and 16th on the &lt;a href=&quot;http:&#x2F;&#x2F;www.doingbusiness.org&#x2F;rankings&quot;&gt;Doing Business ranking&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Do note that Stripe is not available in Estonia yet so it might be a deal breaker if your business depends on it, which might be our case depending on what payment provider we choose for Proppy(article on that coming soon). There is no timeline for it as well:&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en-gb&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;WeAreWizardsIO&quot;&gt;@WeAreWizardsIO&lt;&#x2F;a&gt; No timeline as of yet, sorry! Feel free to sign up here and we&amp;#39;ll notify you as soon as we&amp;#39;re ready &lt;a href=&quot;https:&#x2F;&#x2F;t.co&#x2F;vbuzSSxceJ&quot;&gt;https:&#x2F;&#x2F;t.co&#x2F;vbuzSSxceJ&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&amp;mdash; Stripe (@stripe) &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;stripe&#x2F;status&#x2F;757525171943120896&quot;&gt;25 July 2016&lt;&#x2F;a&gt;&lt;&#x2F;blockquote&gt;
&lt;script async src=&quot;&#x2F;&#x2F;platform.twitter.com&#x2F;widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;&#x2F;script&gt;
&lt;h4 id=&quot;taxes-2&quot;&gt;Taxes&lt;a class=&quot;zola-anchor&quot; href=&quot;#taxes-2&quot; aria-label=&quot;Anchor link for: taxes-2&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Estonia has a very competitive tax system. The corporation tax is &lt;strong&gt;0%&lt;&#x2F;strong&gt; as long as the money stays into the company bank account. Once you take dividends, those are taxed at a flat &lt;strong&gt;20%&lt;&#x2F;strong&gt; rate.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-to-incorporate-open-bank-account-2&quot;&gt;How to incorporate&#x2F;open bank account&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-incorporate-open-bank-account-2&quot; aria-label=&quot;Anchor link for: how-to-incorporate-open-bank-account-2&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Estonia is at the forefront of e-residency.
It can take a while to get your e-residency card and you need to pick it up yourself, which might mean a trip to Estonia if there isn&#x27;t an Estonian embassy around you.&lt;&#x2F;p&gt;
&lt;p&gt;Incorporation can be done online and takes about a day.
However, you currently need to go to Estonia to open a bank account but starting September it will apparently be possible to open one through Skype.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.leapin.eu&quot;&gt;Leapin&lt;&#x2F;a&gt; is recommended by the e-residency website and offers incorporation and accounting for less than 100€ per month. They prefer single shareholder but can make exceptions for multiple ones, provided that all of them have e-residency.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;singapore&quot;&gt;Singapore&lt;a class=&quot;zola-anchor&quot; href=&quot;#singapore&quot; aria-label=&quot;Anchor link for: singapore&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;trivia-3&quot;&gt;Trivia&lt;a class=&quot;zola-anchor&quot; href=&quot;#trivia-3&quot; aria-label=&quot;Anchor link for: trivia-3&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Famous for its ban on chewing-gum and death penalty, this city state is the only country on that list that isn&#x27;t in the EU.&lt;&#x2F;p&gt;
&lt;p&gt;It takes the 1st place on the &lt;a href=&quot;http:&#x2F;&#x2F;www.doingbusiness.org&#x2F;rankings&quot;&gt;Doing Business ranking&lt;&#x2F;a&gt; and, more sadly, the 154th position on the &lt;a href=&quot;https:&#x2F;&#x2F;rsf.org&#x2F;en&#x2F;ranking&quot;&gt;World Press Freedom index&lt;&#x2F;a&gt;. It also probably tops the ranking of mall per inhabitant from my own experience as a tourist there.&lt;&#x2F;p&gt;
&lt;p&gt;The immigration process seems pretty open and you should be able to relocate to Singapore without too many issues should you want to do that. I liked Singapore as a tourist but I couldn&#x27;t say whether it would be a nice place to live or not.&lt;&#x2F;p&gt;
&lt;p&gt;Since Singapore isn&#x27;t in the EU, you also don&#x27;t have to deal with EU things like VAT MOSS since the EU doesn&#x27;t have jurisdiction over Singapore. Obviously, I&#x27;m not a lawyer so don&#x27;t take my word for it.&lt;&#x2F;p&gt;
&lt;p&gt;Singapore is not doing great on privacy though (&lt;a href=&quot;https:&#x2F;&#x2F;www.privacyinternational.org&#x2F;sites&#x2F;default&#x2F;files&#x2F;2017-12&#x2F;Singapore_UPR_PI_submission_FINAL.pdf&quot;&gt;see report from Privacy International&lt;&#x2F;a&gt;) so we wouldn&#x27;t be able to move there with Passopolis. I&#x27;m leaving this section there though since it could be ok for lots of companies.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;taxes-3&quot;&gt;Taxes&lt;a class=&quot;zola-anchor&quot; href=&quot;#taxes-3&quot; aria-label=&quot;Anchor link for: taxes-3&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The Corporation tax rate depends on the income but has a ceiling of &lt;strong&gt;17%&lt;&#x2F;strong&gt;. In practice though, the rate is very low for &#x27;small&#x27; income, aka most startups&#x2F;companies:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Income (SGD)&lt;&#x2F;th&gt;&lt;th&gt;Effective tax rate&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;100,000&lt;&#x2F;td&gt;&lt;td&gt;0%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;200,000&lt;&#x2F;td&gt;&lt;td&gt;2.15%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;300,000&lt;&#x2F;td&gt;&lt;td&gt;2.83%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;400,000&lt;&#x2F;td&gt;&lt;td&gt;4.25%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;I&#x27;m not going to put all the different tax rates, but even at 10,000,000 SGD is only 16.46%. 1SGD is €0.67 for reference at the time of writing.&lt;&#x2F;p&gt;
&lt;p&gt;Dividends are &lt;strong&gt;tax free&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is pretty much a tax haven.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-to-incorporate-open-bank-account-3&quot;&gt;How to incorporate&#x2F;open bank account&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-incorporate-open-bank-account-3&quot; aria-label=&quot;Anchor link for: how-to-incorporate-open-bank-account-3&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Incorporation is possible remotely but you have to be there in person to create a bank account.&lt;&#x2F;p&gt;
&lt;p&gt;I have looked at &lt;a href=&quot;http:&#x2F;&#x2F;www.rikvin.com&quot;&gt;Rivkin&lt;&#x2F;a&gt; which offers different packages for incorporations, some including a visa.
The whole package (incorporation + registered office address +  virtual office) is about 6000 SGD, roughly £3360 per year.&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned before, you can also relocate yourself to Singapore if you fancy mango lassi and green space.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A few possible choices are available! I would personally remove the UK from the list unless you have the UK citizenship.&lt;&#x2F;p&gt;
&lt;p&gt;That leaves us with Ireland, Estonia, Singapore and, potentially, your own country. The choice is then up to you and your preferences.&lt;&#x2F;p&gt;
&lt;p&gt;We Are Wizards &lt;em&gt;might&lt;&#x2F;em&gt; move to one of these countries because of Brexit and the IP Bill and therefore would like to hear stories from companies&#x2F;startups based in those.&lt;&#x2F;p&gt;
&lt;p&gt;Let us know by email (team AT wearewizards.io) or comment and we can add it to the article!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Introducing Tera, a template engine in Rust</title>
          <pubDate>Fri, 15 Apr 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/introducing-tera/</link>
          <guid>https://www.vincentprouillet.com/blog/introducing-tera/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/introducing-tera/">&lt;p&gt;Back in October 2015, I tried &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;trying-rust-for-web-services&#x2F;&quot;&gt;Rust for web services&lt;&#x2F;a&gt; and found the ecosystem lacking at the time. That&#x27;s why I&#x27;ve been working on porting some of the tools we use in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wearewizards&#x2F;proppyweb&quot;&gt;Proppy&lt;&#x2F;a&gt; to Rust: &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;jsonwebtoken&quot;&gt;jwt&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;bcrypt&quot;&gt;bcrypt&lt;&#x2F;a&gt; (granted that Argon2 seems superior) and a &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;dbmigrate&quot;&gt;migration tool&lt;&#x2F;a&gt;. While I mostly do SPAs these days and don&#x27;t write many templates in the backend, I still need one for some occasions. When using Python, I like &lt;a href=&quot;http:&#x2F;&#x2F;jinja.pocoo.org&#x2F;docs&#x2F;dev&#x2F;&quot;&gt;Jinja2&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;1.9&#x2F;topics&#x2F;templates&#x2F;#the-django-template-language&quot;&gt;Django templates&lt;&#x2F;a&gt;. Here&#x27;s how I attempted to port them to Rust and the result is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;&quot;&gt;Tera&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;goals-and-philosophy&quot;&gt;Goals and philosophy&lt;a class=&quot;zola-anchor&quot; href=&quot;#goals-and-philosophy&quot; aria-label=&quot;Anchor link for: goals-and-philosophy&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned before, the inspiration comes from both Jinja2 and Django templates. As you might know, those two have similar syntax but different philosophies: Django templates are for presentation only and don&#x27;t allow a lot of logic while Jinja2 has more powerful programming constructs in the templates.
I side with Django on this one as complex logic is better put in code than in a template but Django goes a bit too far by even not supporting something like &lt;code&gt;{{ count + 1 }}&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So here are some of the features I want:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;math operations in templates&lt;&#x2F;li&gt;
&lt;li&gt;no macros or other complex logic in the template&lt;&#x2F;li&gt;
&lt;li&gt;beautiful html output out of the box (ie no need for the &lt;code&gt;{{-&lt;&#x2F;code&gt; tags)&lt;&#x2F;li&gt;
&lt;li&gt;simple inheritance&lt;&#x2F;li&gt;
&lt;li&gt;simple to use filters&lt;&#x2F;li&gt;
&lt;li&gt;able to register new tags easily like the &lt;code&gt;{% url ... %}&lt;&#x2F;code&gt; in Django&lt;&#x2F;li&gt;
&lt;li&gt;include partial templates&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;While new tags are definitely logic in the template, that logic would have to be written in Rust and not in a template. That limits reusability but is simpler to understand in the end.&lt;&#x2F;p&gt;
&lt;p&gt;Filters should be kept simple and be limited in scope: variable in, modifier function with optional argument and return a string. The easiest to think of would be uppercase, lowercase, capitalize and more importantly time formatting. Here are some examples of how it should look:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;jinja&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-jinja &quot;&gt;&lt;code class=&quot;language-jinja&quot; data-lang=&quot;jinja&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uppercase &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;}}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;{{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;birthday &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;YYYY-MM-dd&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;}}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Users should be able to add their own filters as well.&lt;&#x2F;p&gt;
&lt;p&gt;In terms of error handling, we cannot do anything if a template cannot be parsed so panicking when encountering an error is fine and template compilation should be done at rustc compile time to ensure everything works perfectly (more on that on the README.md).
On the API side, using Tera should be trivial:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;give a glob that will load all the matching files&lt;&#x2F;li&gt;
&lt;li&gt;register tag&#x2F;filter to Tera&lt;&#x2F;li&gt;
&lt;li&gt;parse all templates&lt;&#x2F;li&gt;
&lt;li&gt;create a context easily (not as simple as a dict obviously though)&lt;&#x2F;li&gt;
&lt;li&gt;call a render method that returns a &lt;code&gt;Result&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In code that would like that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; setting up
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; tera &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Tera::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;app&#x2F;**&#x2F;*.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;register_tag&lt;&#x2F;span&gt;&lt;span&gt;(url_for);
&lt;&#x2F;span&gt;&lt;span&gt;tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;register_tag&lt;&#x2F;span&gt;&lt;span&gt;(retina);
&lt;&#x2F;span&gt;&lt;span&gt;tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;register_filter&lt;&#x2F;span&gt;&lt;span&gt;(number_format);
&lt;&#x2F;span&gt;&lt;span&gt;tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; rendering
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Context::new();
&lt;&#x2F;span&gt;&lt;span&gt;context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;user&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;user);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;tera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;user&#x2F;profile.html&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, context)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Template compilation should only happen once. This can be achieved by using &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;lazy_static&quot;&gt;lazy_static&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how it&#x27;s built now!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-it-s-made&quot;&gt;How it&#x27;s made&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-s-made&quot; aria-label=&quot;Anchor link for: how-it-s-made&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I actually thought of making a template engine after watching the &quot;Lexical Scanning in Go&quot; video by Rob Pike (&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HxaD_trXwRE&quot;&gt;youtube link&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lexer-parser&quot;&gt;Lexer&#x2F;Parser&lt;a class=&quot;zola-anchor&quot; href=&quot;#lexer-parser&quot; aria-label=&quot;Anchor link for: lexer-parser&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This talk explains how the lexing in the &lt;a href=&quot;https:&#x2F;&#x2F;golang.org&#x2F;pkg&#x2F;text&#x2F;template&#x2F;&quot;&gt;template package in the Go standard library&lt;&#x2F;a&gt; is implemented. I thought it was pretty cool and implemented something similar last summer.
For those that don&#x27;t want to watch the video, here&#x27;s a quick summary.&lt;&#x2F;p&gt;
&lt;p&gt;The lexer can be in a few states: inside a block, text, space, number, identifier, string for Tera currently and there are actions that represents what we do and result in a new state. In short, that means we have a state function that takes the lexer as an argument and returns a state function. While this is easy to do in Go, you cannot do reference the type while declaring it but it&#x27;s ok to do so for a struct.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; working
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;StateFn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; Lexer) -&amp;gt; StateFn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; not working
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;StateFn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; Lexer) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;StateFn&amp;gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Thanks for the help on IRC for that one.
The lexer just runs the state function until we reach EOF which in our case is represented by returning &lt;code&gt;None&lt;&#x2F;code&gt; as a state function. Here&#x27;s the whole run method of the lexer:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;loop &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; StateFn(state_fn) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.state;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; state_fn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;is_none&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;break&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.state &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state_fn.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;()(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The actions read the next character and know what to do for each kind of character, eg. finding a number in a variable block will return the &lt;code&gt;lex_number&lt;&#x2F;code&gt; state function. The lexer ouput is a vector of tokens that ends with either a EOF or an error.&lt;&#x2F;p&gt;
&lt;p&gt;You can read the whole lexer &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;blob&#x2F;fddb8a0b82cba7374bd0552fed1cf831b8943395&#x2F;src&#x2F;lexer.rs&quot;&gt;on GitHub&lt;&#x2F;a&gt;, it is actually quite simple and readable.&lt;&#x2F;p&gt;
&lt;p&gt;The parser is quite simple as well. Since we are either in text, in a variable block or in a tag block, we just handle those cases in a loop and use EOF to break. Each &quot;state&quot; knows how to parse itself so the main logic is actually only:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;mut &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;loop &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;peek&lt;&#x2F;span&gt;&lt;span&gt;().kind {
&lt;&#x2F;span&gt;&lt;span&gt;            TokenType::TagStart &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;parse_tag_block&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;            TokenType::VariableStart &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;parse_variable_block&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;            TokenType::Text &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;parse_text&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;            TokenType::Eof &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; break&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_ =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;unreachable!&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        };
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The trickiest bit was handling precedence in blocks so that something like &lt;code&gt;{{1 &#x2F; 2 + 3 * 2 + 42}}&lt;&#x2F;code&gt; would parse as expected. This is done by assigning precedence values to each kind of token and looking forward to see if something with higer precedence is coming. I would be surprised if there was not a bug in there.&lt;&#x2F;p&gt;
&lt;p&gt;The output of the parser is a classic AST.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;context&quot;&gt;Context&lt;a class=&quot;zola-anchor&quot; href=&quot;#context&quot; aria-label=&quot;Anchor link for: context&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Context is where dynamic languages have the upper hand. In python I can just pass a dict &lt;code&gt;{&quot;user&quot;: user, &quot;count&quot;: 1}&lt;&#x2F;code&gt; to Jinja2 or Django and be done with it. In Rust, it can&#x27;t be that easy unfortunately.
Here&#x27;s the same context as above for Tera:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; context &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Context::new();
&lt;&#x2F;span&gt;&lt;span&gt;context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;user&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;user);
&lt;&#x2F;span&gt;&lt;span&gt;context.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;count&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To make that possible, Tera uses &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;serde-rs&#x2F;serde&quot;&gt;serde&lt;&#x2F;a&gt; which means that in the example above, the &lt;code&gt;user&lt;&#x2F;code&gt; variable would have to implement the &lt;code&gt;Serialize&lt;&#x2F;code&gt; trait. This makes Tera annoying to use on non-nightly Rust as compiler plugins are not stable yet. Serde is the future for serialization in Rust so might as well embrace it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rendering&quot;&gt;Rendering&lt;a class=&quot;zola-anchor&quot; href=&quot;#rendering&quot; aria-label=&quot;Anchor link for: rendering&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In the rendering phase, we take the AST from the parser and traverse it, replacing variables with values from the context and handling the various tag blocks. It is also home to some terrible terrible code, namely the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&#x2F;blob&#x2F;fddb8a0b82cba7374bd0552fed1cf831b8943395&#x2F;src&#x2F;render.rs#L126-L241&quot;&gt;&lt;code&gt;eval_condition&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; method that checks &lt;code&gt;if&lt;&#x2F;code&gt; and &lt;code&gt;elif&lt;&#x2F;code&gt; conditions. It has a cyclomatic complexity of 27 apparently.
It also contains the classic test fixing snippet that will be removed after I handle calculations properly:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; TODO: fix properly
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; TODO: add tests for float maths arithmetics
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; result.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;fract&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.01 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; result.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;feedback-on-the-dev-aspect&quot;&gt;Feedback on the dev aspect&lt;a class=&quot;zola-anchor&quot; href=&quot;#feedback-on-the-dev-aspect&quot; aria-label=&quot;Anchor link for: feedback-on-the-dev-aspect&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The more Rust I do, the more I like it. There are times where you might look at the screen blankly for a few minutes and then decide to have a walk instead but it happens less and less. IRC and the &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&quot;&gt;Rust subreddit&lt;&#x2F;a&gt; are very good source for help if you are stuck and they have interesting conversations.&lt;&#x2F;p&gt;
&lt;p&gt;With no hesitation, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Manishearth&#x2F;rust-clippy&quot;&gt;Clippy&lt;&#x2F;a&gt; is the MVP of the development tools I&#x27;m using at the moment across all languages (Rust, Python and TypeScript mostly). It is a linter that catches lots of errors or bad code and shows you how to fix or how to do what you wanted to do but in a clearer way. The team is adding lints continuously so I usually just &lt;code&gt;cargo update&lt;&#x2F;code&gt; my projects every week or so just so I can run the latest &lt;code&gt;clippy&lt;&#x2F;code&gt; on it.&lt;&#x2F;p&gt;
&lt;p&gt;Error messages are usually very good and are getting &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;pull&#x2F;32756&quot;&gt;even better soon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The main things Tera are missing right now are filters and a way to add custom tag blocks. If anyone thinks that it is 2016 and therefore should use a parser combinator, feel free to submit a PR! I also welcome any feedback on Tera design as it doesn&#x27;t have to be a clone of Jinja2 or Django.&lt;&#x2F;p&gt;
&lt;p&gt;Since I am quite busy with our first product &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wearewizards&#x2F;proppyweb&quot;&gt;Proppy&lt;&#x2F;a&gt;, I won&#x27;t have a huge amount of time so any help is welcome.&lt;&#x2F;p&gt;
&lt;p&gt;To finish on a &quot;Rust for web&quot; note, the last main thing I would miss to try it for real is a validation crate that would work &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;Keats&#x2F;32d26f699dcc13ebd41b&quot;&gt;like this gist&lt;&#x2F;a&gt;. We use &lt;a href=&quot;https:&#x2F;&#x2F;marshmallow.readthedocs.org&#x2F;en&#x2F;latest&#x2F;&quot;&gt;marshmallow&lt;&#x2F;a&gt; in proppy and it makes validation API data a breeze. Unfortunately, with compiler plugins being still unstable, I don&#x27;t think I will start working on that. There is a RFC for stabilization though: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&#x2F;pull&#x2F;1566&quot;&gt;Procedural macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Looking at payments solution for SaaS in Europe</title>
          <pubDate>Thu, 10 Dec 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/looking-at-payments-in-europe/</link>
          <guid>https://www.vincentprouillet.com/blog/looking-at-payments-in-europe/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/looking-at-payments-in-europe/">&lt;p&gt;As we are working on our product (if you work in an agency, we&#x27;d love to have a chat with you! send us a mail at team AT wearewizards.io), I was curious to see the payments landscape in Europe, having read so much about &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;vatmess&quot;&gt;#VATMESS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In Europe, before 2015, the supplier would charge the VAT rate of their own country to their customers: for example if the supplier was based in the UK and the customer in France, the supplier would charge the UK VAT rate.&lt;&#x2F;p&gt;
&lt;p&gt;Since 2015 however, the supplier now has to charge the VAT rate of the customer&#x27;s country and report sales to that country. This change was made to prevent unfair competition from countries with a lower VAT rate, with big companies moving to Luxembourg for example. Regulators only forgot to set a lower limit (they are apparently now thinking of a £5k limit) and didn&#x27;t think of SME or thought about it and decided that crippling European businesses would be a good idea.
Note that this change only applies to B2C customers.&lt;&#x2F;p&gt;
&lt;p&gt;This article will focus on what it means for a UK-based VAT-registered company as it is our case.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;business-side&quot;&gt;Business side&lt;a class=&quot;zola-anchor&quot; href=&quot;#business-side&quot; aria-label=&quot;Anchor link for: business-side&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If the customer is a business and is VAT registered, just getting the VAT number and checking it should be enough. However, companies paying without giving their VAT number or non-VAT registered ones should be treated as B2C.&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned in the introduction, we need to charge the VAT of the customer&#x27;s country which means we need to locate them.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some examples of pieces of informations we can use to identify a customer&#x27;s location:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;billing address&lt;&#x2F;li&gt;
&lt;li&gt;IP address geolocation&lt;&#x2F;li&gt;
&lt;li&gt;bank location&lt;&#x2F;li&gt;
&lt;li&gt;card location&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note that the legislation requires to have two non-conflicting items of information and keep them for 10 (!) years.
Without the non-conflicting information you have to reject the transaction. You could possibly ask for the user to send some proof by mail but that&#x27;s a terrible user experience.&lt;&#x2F;p&gt;
&lt;p&gt;If we manage to get customers to pay us, we then need to separate sales by country and apply the right VAT. This gives us the amount of tax we need to pay for each country.&lt;&#x2F;p&gt;
&lt;p&gt;The UK has put in place something called the VAT Mini One Stop Shop or VAT MOSS (https:&#x2F;&#x2F;www.gov.uk&#x2F;guidance&#x2F;register-and-use-the-vat-mini-one-stop-shop). This allows businesses to register in only one country and let it redistribute the money after filing a VAT return for other countries on a quarterly basis with the amounts we calculated.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementing&quot;&gt;Implementing&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementing&quot; aria-label=&quot;Anchor link for: implementing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have an overview of what the VAT system looks like, let&#x27;s have a look at the solutions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;payment-providers&quot;&gt;Payment providers&lt;a class=&quot;zola-anchor&quot; href=&quot;#payment-providers&quot; aria-label=&quot;Anchor link for: payment-providers&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;None of these (except FastSpring) actually help with the VAT mess but are still needed.
There are a few alternatives here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;stripe.com&quot;&gt;Stripe&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.braintreepayments.com&#x2F;&quot;&gt;Braintree&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.paymill.com&#x2F;&quot;&gt;Paymill&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.fastspring.com&#x2F;&quot;&gt;FastSpring&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Paymill is based in Europe. Stripe is extremely easy to setup and get started. I haven&#x27;t used the other two so I can&#x27;t comment on those but it seems you need to be approved for a merchant account for them which means some some wait.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the advertised rates (you might get a better one by talking with them):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Paymill: 2.95% + 0.28€&lt;&#x2F;li&gt;
&lt;li&gt;Braintree: 2.4% + 20p (first £30k free) for EU cards, 3.4% + 20p for non EU cards&lt;&#x2F;li&gt;
&lt;li&gt;Stripe: 1.9% + 20p for UK cards and 2.9% + 20p for foreign cards&lt;&#x2F;li&gt;
&lt;li&gt;FastSpring: 5.9% + $.95 per transaction&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s use a small example to see the difference: £10k sales with £5k from the UK, £3k from NA and £2k from Europe with let&#x27;s say 100 transactions:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;paymill &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.95 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.20 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# assuming £1=1€
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;315
&lt;&#x2F;span&gt;&lt;span&gt;braintree &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.4 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;7000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3.4 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.20 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;290
&lt;&#x2F;span&gt;&lt;span&gt;stripe &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.9 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.9 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.20 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;260
&lt;&#x2F;span&gt;&lt;span&gt;fastspring &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5.95 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10000 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.63 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# today&amp;#39;s rate $0.95 &amp;gt; £0.63
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;658
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Stripe seems to give the best price but keep in mind you cand probably get better rates than the advertised one (at least for Paymill according to the employee I met ages ago).
FastSpring is pretty damn expensive but seems to have European VAT handling built-in.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;handling-vat&quot;&gt;Handling VAT&lt;a class=&quot;zola-anchor&quot; href=&quot;#handling-vat&quot; aria-label=&quot;Anchor link for: handling-vat&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;implementing-it-yourself&quot;&gt;Implementing it yourself&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementing-it-yourself&quot; aria-label=&quot;Anchor link for: implementing-it-yourself&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;In all cases, you need to ask for the customer&#x27;s country in the checkout form to serve as an equivalent of a billing address. You can prefill that by using the IP address for better UX.&lt;&#x2F;p&gt;
&lt;p&gt;You need to handle two cases:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Customer provides a VAT number&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Check that the VAT number is correct and match the country given.
If it is valid, you only need to add VAT if the customer is based in the same country as you.
If the customer is not in the same country, do not charge any VAT.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Customer doesn&#x27;t provide VAT number&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Compare the country selected with the IP location.
If it matches continue otherwise compare the country filled with the bank country.
If it matches continue otherwise ask the customer to send a fax with their certificate of birth and a passport.
More seriously: what should be done in the last case?&lt;&#x2F;p&gt;
&lt;p&gt;There are APIs to get up-to-date VAT rates and check VAT numbers but you could use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;iconfinder&#x2F;pyvat&quot;&gt;pyvat&lt;&#x2F;a&gt; for example to handle all of that for you. Keep in mind that you do not charge VAT for clients outside of the EU.&lt;&#x2F;p&gt;
&lt;p&gt;While this is annoying, it doesn&#x27;t seem like a huge amount of work.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;using-a-third-party&quot;&gt;Using a third party&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-a-third-party&quot; aria-label=&quot;Anchor link for: using-a-third-party&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;If you prefer using off-the-shelf solutions, a few exist that handle the VAT aspect (note that I haven&#x27;t used any of them):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;recurly.com&#x2F;&quot;&gt;Recurly&lt;&#x2F;a&gt;: $99&#x2F;month and 10¢ per transaction + 1.25% of revenue (on top of payment gateway)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;quaderno.io&#x2F;&quot;&gt;Quaderno&lt;&#x2F;a&gt;: $29&#x2F;month on top of your payment provider, provides a widget for checkout that handles European VAT rules&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.taxamo.com&quot;&gt;Taxamo&lt;&#x2F;a&gt;: £40&#x2F;month for all the regions, pretty limited number of country as it&#x27;s missing Canada and Australia for example. Doesn&#x27;t actually show what the product is but included for completeness&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.chargebee.com&quot;&gt;Chargebee&lt;&#x2F;a&gt;: $49&#x2F;month on top of a payment provider&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Out of those, Quaderno seems to be the best choice and is fairly cheap. Recurly looks neat as well but is quite expensive without a clear added value on what it adds to let&#x27;s say Stripe recurrent billing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As a developer I am tempted to write my own version but Quaderno seems like a good fit and is cheap enough for a B2B SaaS that it might be worth avoiding the hassle of coding it.&lt;&#x2F;p&gt;
&lt;p&gt;We are more interested in feedback really, quite curious of how European SaaS are handling payments after this year&#x27;s change in VAT.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Writing my first Rust crate: jsonwebtoken</title>
          <pubDate>Wed, 04 Nov 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/writing-my-first-crate/</link>
          <guid>https://www.vincentprouillet.com/blog/writing-my-first-crate/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/writing-my-first-crate/">&lt;p&gt;After &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;trying-rust-for-web-services&#x2F;&quot;&gt;looking into Rust for webservices&lt;&#x2F;a&gt;, I concluded that, while it was not mature enough yet in my opinion, the language itself is quite nice and I would be interested in writing more of it. So here it is, my first crate (the name for packages in Rust): &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;jsonwebtoken&quot;&gt;jsonwebtoken&lt;&#x2F;a&gt; (code on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;keats&#x2F;rust-jwt&quot;&gt;github&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-jwts&quot;&gt;What are JWTs&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-are-jwts&quot; aria-label=&quot;Anchor link for: what-are-jwts&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First of all a very quick introduction on JSON Web Token (JWT). If you already know about them you can skip this.&lt;&#x2F;p&gt;
&lt;p&gt;JWTs are a way to transmit data using JSON — hence the name — and are mostly used for APIs and token based authentication. You can read the standard &lt;a href=&quot;http:&#x2F;&#x2F;self-issued.info&#x2F;docs&#x2F;draft-ietf-oauth-json-web-token.html&quot;&gt;there&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than having a session in a database, you would store some non-sensitive data in your token that allow you to identify the user.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what a JWT looks like (with the fields shortened for example&#x27;s sake):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; header.payload.signature
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;eyJhbGciOiJIUzI1Ni&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;eyJzdWIiOiIxMjM0NTY3ODkwIn0&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Rq8IxqeX7eA6GgYxlcHdP
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;From the code above, you can see that a JWT has 3 parts, separated by a &lt;code&gt;.&lt;&#x2F;code&gt;.
All of those parts are base64 encoded, which means it is trivial to decode (remember the non-sensitive data point above?).&lt;&#x2F;p&gt;
&lt;p&gt;The first is the header, that typically contains the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;alg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;HS256&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; which algorithm was used to sign
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;typ&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;JWT&amp;quot;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; actually optional
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The second is the claims object, which contains an arbitrary JSON object. There are
some reserved claim name such as &lt;code&gt;exp&lt;&#x2F;code&gt; for an expiration timestamp but none of them are
mandatory. Typically in an app, you would store the user id along as some token metadata such as exp mentioned above for example.&lt;&#x2F;p&gt;
&lt;p&gt;The last part is the signature which is obtained the following way in pseudo-code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;payload &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;base64&lt;&#x2F;span&gt;&lt;span&gt;(header) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;base64&lt;&#x2F;span&gt;&lt;span&gt;(claims)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;secret&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;is &lt;&#x2F;span&gt;&lt;span&gt;your secret private key
&lt;&#x2F;span&gt;&lt;span&gt;signature &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Hmac&lt;&#x2F;span&gt;&lt;span&gt;(payload, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;secret&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, Sha256)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see it&#x27;s quite a convenient way to transfer some data and the signature ensures that the payload was not tampered with (there are some issues though, such as &lt;a href=&quot;https:&#x2F;&#x2F;auth0.com&#x2F;blog&#x2F;2015&#x2F;03&#x2F;31&#x2F;critical-vulnerabilities-in-json-web-token-libraries&#x2F;&quot;&gt;this vulnerability&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enter-jsonwebtoken&quot;&gt;Enter jsonwebtoken&lt;a class=&quot;zola-anchor&quot; href=&quot;#enter-jsonwebtoken&quot; aria-label=&quot;Anchor link for: enter-jsonwebtoken&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I was implementing JWT in a Go project using the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dgrijalva&#x2F;jwt-go&quot;&gt;jwt-go&lt;&#x2F;a&gt; library when I realised a JWT library was something simple enough that I could try in Rust.&lt;&#x2F;p&gt;
&lt;p&gt;We can see there are already &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;search?q=jwt&quot;&gt;crates&lt;&#x2F;a&gt; for that available but &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;GildedHonour&#x2F;frank_jwt&quot;&gt;one&lt;&#x2F;a&gt; only allows string types and the other put too much important on the registered claims to my liking.&lt;&#x2F;p&gt;
&lt;p&gt;I didn&#x27;t implement the standard to the letter as I wanted to keep it simple for now and not support some part of it (having &lt;code&gt;none&lt;&#x2F;code&gt; a valid value for the alg header makes no sense to me therefore is not supported).&lt;&#x2F;p&gt;
&lt;p&gt;Compared to the Go library (which is quite good!), using Rust allows leveraging generics to have type-safe claims.
For example, the claims in jwt-go are stored in a &lt;code&gt;map[string]interface{}&lt;&#x2F;code&gt; which is the only way to do so and therefore need type assertion when decoding claims to access its true type. The next version (v3 at the time of writing) seem to allow passing a struct though.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how jsonwebtoken is used in practice, taken from the example in the crate:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;extern crate&lt;&#x2F;span&gt;&lt;span&gt; jsonwebtoken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as&lt;&#x2F;span&gt;&lt;span&gt; jwt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;extern crate&lt;&#x2F;span&gt;&lt;span&gt; rustc_serialize;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;jwt::{
&lt;&#x2F;span&gt;&lt;span&gt;    Algorithm,
&lt;&#x2F;span&gt;&lt;span&gt;    encode,
&lt;&#x2F;span&gt;&lt;span&gt;    decode
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Deriving RustcEncodable and RustcDecodable make Claims satisfy the jwt::Part trait
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span&gt;(Debug, RustcEncodable, RustcDecodable)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Claims &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sub&lt;&#x2F;span&gt;&lt;span&gt;: String,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;company&lt;&#x2F;span&gt;&lt;span&gt;: String
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; my_claims &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Claims {
&lt;&#x2F;span&gt;&lt;span&gt;        sub: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;b@b.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;        company: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;ACME&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; key &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;secret&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; token &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= match &lt;&#x2F;span&gt;&lt;span&gt;encode::&amp;lt;Claims&amp;gt;(my_claims, key.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(), Algorithm::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;HS256&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(t) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; t,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;panic!&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; or let token = try!(encode::&amp;lt;Claims&amp;gt;(my_claims, key.to_owned(), Algorithm::HS256));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; And then decode it back into a Claims object
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; claims &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= match &lt;&#x2F;span&gt;&lt;span&gt;decode::&amp;lt;Claims&amp;gt;(token.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(), key.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;(), Algorithm::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;HS256&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(c) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; c,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(err) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; match&lt;&#x2F;span&gt;&lt;span&gt; err {
&lt;&#x2F;span&gt;&lt;span&gt;            Error::InvalidToken &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;panic!&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Example on how to handle a specific error
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_ =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;panic!&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; or let claims = try!(decode::&amp;lt;Claims&amp;gt;(token.to_owned(), key.to_owned(), Algorithm::HS256));
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On the other hand, decoding the token gives you an instance of the &lt;code&gt;Claims&lt;&#x2F;code&gt; struct declared in the function and thanks to the &lt;code&gt;Result&lt;&#x2F;code&gt; type, we are certain that if we are in the &lt;code&gt;Ok()&lt;&#x2F;code&gt; branch, the variable contains valid data.&lt;&#x2F;p&gt;
&lt;p&gt;How cool is that? And we can use any struct we want as long as the struct we use satisfy the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;rust-jwt&#x2F;blob&#x2F;6ae77c0b068328c47febe4169d6d28c0c66ba101&#x2F;src&#x2F;lib.rs#L29-L47&quot;&gt;&lt;code&gt;Part&lt;&#x2F;code&gt; trait&lt;&#x2F;a&gt;, which is done automatically by deriving &lt;code&gt;RustcEncodable&lt;&#x2F;code&gt; and &lt;code&gt;RustcDecodable&lt;&#x2F;code&gt;.
I&#x27;m guessing other languages with generics implement similar things for their JWT libraries but it feels cleaner to me compared to the current version of the Go one.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thoughts&quot;&gt;Thoughts&lt;a class=&quot;zola-anchor&quot; href=&quot;#thoughts&quot; aria-label=&quot;Anchor link for: thoughts&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here are some thoughts on the process of creating that crate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cargo&quot;&gt;Cargo&lt;a class=&quot;zola-anchor&quot; href=&quot;#cargo&quot; aria-label=&quot;Anchor link for: cargo&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Cargo is really cool. It probably is the smoothest package manager of all the programming languages that I&#x27;ve tried.
From installing crates to publishing the package on &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;crates.io&lt;&#x2F;a&gt;, everything works.&lt;&#x2F;p&gt;
&lt;p&gt;I think the main thing missing for me is built-in vendoring, to not be dependent on a third party (crates.io).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;error-handling&quot;&gt;Error handling&lt;a class=&quot;zola-anchor&quot; href=&quot;#error-handling&quot; aria-label=&quot;Anchor link for: error-handling&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;One of the most annoying thing for me in Go is all those &lt;code&gt;if err != nil&lt;&#x2F;code&gt;. After trying Rust, I wish there was an equivalent of the &lt;code&gt;try!&lt;&#x2F;code&gt; macro so I could get rid of all that code cluttering my functions.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;Result&lt;&#x2F;code&gt; type makes error checking mandatory to handle them, like in the example above. Pattern matching also has to be exhaustive, meaning that every possible value of the type I am matching has to be handled, from numbers to enum members and errors.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-and-benchmark-support&quot;&gt;Testing and benchmark support&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing-and-benchmark-support&quot; aria-label=&quot;Anchor link for: testing-and-benchmark-support&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Having tests built-in the language and the package manager is very nice, ensuring there is no barrier to tests.&lt;&#x2F;p&gt;
&lt;p&gt;It is missing an easy setup and teardown method though but I think I have seen macros for that and, hopefully, someone will make a tool similar to the amazing &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smartystreets&#x2F;goconvey&quot;&gt;goconvey&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;to-owned&quot;&gt;.to_owned()&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-owned&quot; aria-label=&quot;Anchor link for: to-owned&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If you look at that the code, it is littered with to_owned() calls which converts a string literal (&lt;code&gt;&amp;amp;str&lt;&#x2F;code&gt;) into a &lt;code&gt;String&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I wish there was a quickway to create a &lt;code&gt;String&lt;&#x2F;code&gt; (maybe there is but I haven&#x27;t found it yet other than writing a macro) such as typing a string into backtick for example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; secret &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; `secret`;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Probably a silly idea but it would make things like tests less noisy.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;community&quot;&gt;Community&lt;a class=&quot;zola-anchor&quot; href=&quot;#community&quot; aria-label=&quot;Anchor link for: community&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The IRC channels (&lt;code&gt;#rust&lt;&#x2F;code&gt; and &lt;code&gt;#rust-beginners&lt;&#x2F;code&gt; on &lt;code&gt;irc.mozilla.org&lt;&#x2F;code&gt;) and the &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&quot;&gt;rust subreddit&lt;&#x2F;a&gt; were very helpful.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;That was quite a pleasant experience!&lt;&#x2F;p&gt;
&lt;p&gt;While I&#x27;m still very much a complete newbie in Rust, I&#x27;m looking forward to making another library in Rust.
It will probably some kind of port of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mattes&#x2F;migrate&quot;&gt;migrate&lt;&#x2F;a&gt;, ie a tool to handle SQL migrations.
If you have any comments or find a bug, feel free to open an issue or submit a pull request to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;keats&#x2F;rust-jwt&quot;&gt;the repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, thanks to the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jpadilla&#x2F;pyjwt&quot;&gt;pyjwt library&lt;&#x2F;a&gt; for the API inspiration!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Trying Rust for web services</title>
          <pubDate>Thu, 01 Oct 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/trying-rust-for-web-services/</link>
          <guid>https://www.vincentprouillet.com/blog/trying-rust-for-web-services/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/trying-rust-for-web-services/">&lt;p&gt;Developing web apps in dynamic languages is a breeze when using frameworks like &lt;a href=&quot;https:&#x2F;&#x2F;www.djangoproject.com&#x2F;&quot;&gt;Django&lt;&#x2F;a&gt; for Python.
The downsides are that software written in dynamic languages is harder, at least in my opinion, to maintain, to refactor and you also need to write tests to cover potential errors that would simply not be possible with a compiler.&lt;&#x2F;p&gt;
&lt;p&gt;While I would use Flask or Django for a small project, I would definitely prefer having help from a compiler for a long-lived product. The fact that compiled languages are generally faster than dynamic ones is a nice bonus too.&lt;&#x2F;p&gt;
&lt;p&gt;The two choices in my mind right now for a compiled language are Go and Rust. Some (ie Tom) would say Haskell, others would say Scala but in the end it&#x27;s down to preferences.&lt;&#x2F;p&gt;
&lt;p&gt;I previously used Go for a few projects and I quite liked it despite some annoying quirks such as package management (might be solved by &lt;code&gt;gb&lt;&#x2F;code&gt; now) and seeing &lt;code&gt;interface {}&lt;&#x2F;code&gt; in some libraries to get around the weak type system. On the other hand, I have been following &lt;a href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; development for quite a while but didn&#x27;t play with it more than a couple of toy programs. I was keen to see if it could work as the backend for a web app, which in short means a HTTP server receiving and sending JSON while talking to a Postgres database. There are obviously an awful lot of other things but let&#x27;s keep it simple for now.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-demo&quot;&gt;The demo&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-demo&quot; aria-label=&quot;Anchor link for: the-demo&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can see the project at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webrust&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;webrust&lt;&#x2F;a&gt;.
This is a simple webapp with one endpoint that responds to either a GET or a POST. The GET handler will return all the passwords in the table and the POST will insert a record into the table and return a 201 status code.&lt;&#x2F;p&gt;
&lt;p&gt;For the HTTP framework, I have used &lt;a href=&quot;http:&#x2F;&#x2F;ironframework.io&#x2F;&quot;&gt;Iron&lt;&#x2F;a&gt; which was simple to use, granted I only created 2 routes and had no middlewares but there are other alternative such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Ogeon&#x2F;rustful&quot;&gt;rustful&lt;&#x2F;a&gt; and &lt;a href=&quot;http:&#x2F;&#x2F;nickel.rs&#x2F;&quot;&gt;nickle.rs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For the postgres side, there is a crate (the term for a package in Rust) called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sfackler&#x2F;rust-postgres&quot;&gt;postgres&lt;&#x2F;a&gt;. I&#x27;m also using a pool manager called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sfackler&#x2F;r2d2&quot;&gt;r2d2&lt;&#x2F;a&gt; because I didn&#x27;t know about &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;struct.Arc.html&quot;&gt;Arc&lt;&#x2F;a&gt; and couldn&#x27;t pass the connection itself as it wasn&#x27;t thread safe.&lt;&#x2F;p&gt;
&lt;p&gt;The Makefile for Postgres was taken from another Iron + Postgres project I have found on Github: rustwebapp (link now dead).&lt;&#x2F;p&gt;
&lt;p&gt;Keep in mind I&#x27;m a newbie in Rust so there are probably lots of things I&#x27;m doing wrong in there, feel free to point them out!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; the server is currently quite slow, only handling around 6.5k req&#x2F;s for the GET handler, removing the database part makes it shoot up to 70k req&#x2F;s so something must be going wrong somewhere around postgres&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;what-i-liked&quot;&gt;What I liked&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-liked&quot; aria-label=&quot;Anchor link for: what-i-liked&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;&quot;&gt;Cargo&lt;&#x2F;a&gt;, Rust package manager, works pretty damn well and its configuration file &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; is easy to write and understand. It is also used to build and run your package.&lt;&#x2F;p&gt;
&lt;p&gt;The language itself is very nice and easy to get started, granted this demo is very simple and doesn&#x27;t have a single lifetime. Every single time the compiler didn&#x27;t complain, whatever I wanted to do was working and whenever it complained, the message was clear enough for me to realize what was happening.
This is not going to be exhaustive by any means but here are the things I liked and disliked the most.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;result-and-option-types&quot;&gt;Result and Option types&lt;a class=&quot;zola-anchor&quot; href=&quot;#result-and-option-types&quot; aria-label=&quot;Anchor link for: result-and-option-types&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Every Go developers has been caught forgetting the &lt;code&gt;if err != nil&lt;&#x2F;code&gt; when calling a function, every Python developers has seen exceptions that they didn&#x27;t realise could happen. Rust (and some functional languages it&#x27;s borrowing the concept from) bake those in the language, using the &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;result&#x2F;&quot;&gt;Result&lt;&#x2F;a&gt; type.
In practice, this means that by returning this type, the caller &lt;strong&gt;has&lt;&#x2F;strong&gt; to handle the possible errors. It wouldn&#x27;t even compile if you didn&#x27;t as we will see later, unless you use &lt;code&gt;.unwrap()&lt;&#x2F;code&gt; which is a way to get panics if there is an error.&lt;&#x2F;p&gt;
&lt;p&gt;The Option types is the same as the Maybe in FP, it represents the possibility of a value which Javascript would benefit greatly from for example (&lt;code&gt;undefined is not a function&lt;&#x2F;code&gt; anyone?).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pattern-matching&quot;&gt;Pattern matching&lt;a class=&quot;zola-anchor&quot; href=&quot;#pattern-matching&quot; aria-label=&quot;Anchor link for: pattern-matching&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The easiest way to handle the above types is called &lt;em&gt;pattern matching&lt;&#x2F;em&gt; and is awesome in Rust.
As an example from the demo, here&#x27;s how to handle the Result from &lt;code&gt;list_passwords&lt;&#x2F;code&gt; that fetches all the passwords from the table in our handler:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span&gt;dal::list_passwords(conn) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(passwords) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; response_payload &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;try_or_500!&lt;&#x2F;span&gt;&lt;span&gt;(serde_json::to_string(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;passwords));
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(Response::with((status::Ok, response_payload)))
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(e) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(Response::with((status::InternalServerError)))
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; Rust automatically returns the last value, in that case the Ok() ones. Iron handler return type is a Result so I should probably return an Err but it currently works well enough™&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As mentioned earlier, Rust forces us to handle the result properly: removing the Err branch errors with the following message &lt;code&gt;error: non-exhaustive patterns: Err(_) not covered [E0004]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Pattern matching is obviously not limited to Result and Option and can be used anywhere you would want a if&#x2F;else or a switch.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;macros&quot;&gt;Macros&lt;a class=&quot;zola-anchor&quot; href=&quot;#macros&quot; aria-label=&quot;Anchor link for: macros&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Macros are a way to abstract some code but, instead of a function call, the macro is expanded where it&#x27;s called at compilation time. In practice it means you can simplify code without the overhead of calling a function. The most used macro is probably &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;macro.try!.html&quot;&gt;try!&lt;&#x2F;a&gt; which returns the error if there is one or gives the result value otherwise. Here&#x27;s an example from the demo: while trying things out I ended up with the following two lines in each http handler to get a connection to postgres.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; pool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; req.get::&amp;lt;PRead&amp;lt;db::PostgresDB&amp;gt;&amp;gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; conn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As you can see, I use &lt;code&gt;unwrap&lt;&#x2F;code&gt; which means it can panic in case of errors. I wrote the following macro to clean it up:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; db.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Gets a connection from the pool from the given request or returns a 500
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;macro_rules! &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;get_pg_connection &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$req&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;expr&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$req&lt;&#x2F;span&gt;&lt;span&gt;.get::&amp;lt;persistent::Read&amp;lt;db::PostgresDB&amp;gt;&amp;gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(pool) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; match&lt;&#x2F;span&gt;&lt;span&gt; pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(conn) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; conn,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Couldn&amp;#39;t get a connection to pg!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(Response::with((status::InternalServerError)));
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Couldn&amp;#39;t get the pg pool from the request!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span&gt;(Response::with((status::InternalServerError)));
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; main.rs
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we can use the macro that way in a handler, notice the ! to indicate it&amp;#39;s a macro
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; conn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;get_pg_connection!&lt;&#x2F;span&gt;&lt;span&gt;(req);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The last line in the snippet will get a connection or will return a 500 if anything fails in the middle.&lt;&#x2F;p&gt;
&lt;p&gt;You can even have macros checking &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sfackler&#x2F;rust-postgres-macros&quot;&gt;SQL syntax&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lfairy&#x2F;maud&quot;&gt;HTML one&lt;&#x2F;a&gt; at compile time! Neat!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-didn-t-like&quot;&gt;What I didn&#x27;t like&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-i-didn-t-like&quot; aria-label=&quot;Anchor link for: what-i-didn-t-like&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I think it can be summed up in one word: documentation.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the crates I have looked at (except the postgres one) have next to no real documentation, only a maze of links to click in the generated rustdoc that pulls the in-code documentation into a nice looking website. This is useful if I want to know more details about a type or a trait but doesn&#x27;t help me understanding how am I supposed to use it. Having to go through the tests or the code directly to see what I am supposed to do for every crate instead of having a &quot;How to use&quot; documentation is annoying.&lt;&#x2F;p&gt;
&lt;p&gt;The provided documentation can also be outdated as it is currently not integrated with Cargo. There is an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;crates.io&#x2F;issues&#x2F;91&quot;&gt;open issue&lt;&#x2F;a&gt; for that though.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rust-for-a-backend-yet&quot;&gt;Rust for a backend yet?&lt;a class=&quot;zola-anchor&quot; href=&quot;#rust-for-a-backend-yet&quot; aria-label=&quot;Anchor link for: rust-for-a-backend-yet&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I don&#x27;t think I would use Rust if I had to choose a backend language right now. The toy project was intentionally small but there are lots of other things needed for a backend not shown here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;sending emails&lt;&#x2F;em&gt;: I found &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;amousset&#x2F;rust-smtp&quot;&gt;rust-smtp&lt;&#x2F;a&gt; but haven&#x27;t tried it&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;templates&lt;&#x2F;em&gt;: needed for example for emails, there is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sunng87&#x2F;handlebars-rust&quot;&gt;handlebars&lt;&#x2F;a&gt; and if I have time I would like to port a subset of &lt;a href=&quot;http:&#x2F;&#x2F;jinja.pocoo.org&#x2F;docs&#x2F;dev&#x2F;&quot;&gt;Jinja2&lt;&#x2F;a&gt;. There is also some compile-time HTML templating using macros such as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lfairy&#x2F;maud&quot;&gt;maud&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;serialization e.g. between services&lt;&#x2F;em&gt; : there is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stepancheg&#x2F;rust-protobuf&quot;&gt;rust-protobuf&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;database migrations&lt;&#x2F;em&gt;: we could probably use &lt;a href=&quot;http:&#x2F;&#x2F;alembic.readthedocs.org&#x2F;en&#x2F;latest&#x2F;&quot;&gt;Alembic&lt;&#x2F;a&gt;, a python library&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;logging&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;error reporting&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;and probably a bunch of other things that I can&#x27;t think of right now&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are also a few open questions where I haven&#x27;t spent the time to look for the answer:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;testing&lt;&#x2F;em&gt;: are there hooks available somewhere to truncate the database between tests?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;mocking&lt;&#x2F;em&gt;: often you want to mock things in test, how would that work in Rust?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I think that, with a bit more time, the ecosystem will be good enough to make Rust a good first choice but it is not there right now. If I had to start a project now, I would lean towards Go but who knows for the one after!&lt;&#x2F;p&gt;
&lt;p&gt;As mentioned before, I&#x27;m a Rust beginner so do not hesitate to correct me and I would definitely appreciate PRs on the demo to see how it could improve.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Hacker News effect examined</title>
          <pubDate>Sun, 14 Jun 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/looking-at-hackernews-effect/</link>
          <guid>https://www.vincentprouillet.com/blog/looking-at-hackernews-effect/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/looking-at-hackernews-effect/">&lt;blockquote&gt;
&lt;p&gt;Note: the original article was published on my old company blog which is no longer live. I&#x27;ve changed the URLs so they don&#x27;t 404&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Last week my article &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;trying-to-replace-json-with-protobuf&#x2F;&quot;&gt;Using Protobuf instead of JSON to communicate with a frontend&lt;&#x2F;a&gt; ended up being number one on &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&quot;&gt;Hacker News&lt;&#x2F;a&gt; and we saw a crazy amount of traffic coming on this blog.&lt;&#x2F;p&gt;
&lt;p&gt;I thought that it would be interesting to analyze some of that data so here it is.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-data&quot;&gt;The data&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-data&quot; aria-label=&quot;Anchor link for: the-data&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It is coming from Google Analytics and while I could just paste screenshots, that wouldn&#x27;t be any fun so I exported some of it as CSVs and used them in the notebook you can see below. The data is over the past month to have more insights on various social &quot;strategies&quot; (or rather, lack of) as you will see in a bit.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;matplotlib inline
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;matplotlib.pyplot &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;plt
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;numpy &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;np
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;pandas &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;pd
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;seaborn  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# make charts prettier and more readable
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Let&amp;#39;s look at the number of sessions first
&lt;&#x2F;span&gt;&lt;span&gt;sessions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;sessions.csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;index_col&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Day Index&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;parse_dates&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;intify_sessions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;df&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    GA returns big numbers as string like 1,000
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    That won&amp;#39;t do here we need ints
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;df[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;,&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;regex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;astype&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;int&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# We need to ensure we only have ints
&lt;&#x2F;span&gt;&lt;span&gt;sessions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;intify_sessions&lt;&#x2F;span&gt;&lt;span&gt;(sessions)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# We will annotate the data with our articles to see the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# difference between them
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# The number at the end of the tuples is the y offset
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# for the annotation
&lt;&#x2F;span&gt;&lt;span&gt;articles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[
&lt;&#x2F;span&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;2015-05-11&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Berg&amp;#39;s Little Printer&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;2015-05-19&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Comparing the weather of places I&amp;#39;ve lived in&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;130&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;2015-05-25&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;My experience of using NixOps as an Ansible user&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;135&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;2015-06-05&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Using Protobuf instead of JSON to communicate with a frontend&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;dates &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[date.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;isoformat&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;date &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;sessions.index.date.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;tolist&lt;&#x2F;span&gt;&lt;span&gt;()]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;ax &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;111&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;tick_params&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;axis&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;both&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;which&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;major&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labelsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;sessions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;x_compat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Daily sessions on the blog&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;(date, title, offset) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;articles:
&lt;&#x2F;span&gt;&lt;span&gt;    index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;dates.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;index&lt;&#x2F;span&gt;&lt;span&gt;(date)
&lt;&#x2F;span&gt;&lt;span&gt;    number_sessions &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;sessions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;][index]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;ax.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;annotate&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt; sessions&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;(title, number_sessions),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;xy&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(sessions.index[index], sessions[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;][index]),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;horizontalalignment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;right&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;verticalalignment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;center&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;xytext&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;00&lt;&#x2F;span&gt;&lt;span&gt;, offset),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;textcoords&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;offset points&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;arrowprops&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;dict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;arrowstyle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;-|&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_1_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_1_0.png&quot; alt=&quot;GA stats: sessions&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Here are the links of the articles for reference (I&#x27;ll refer to them by their number in the list below from now on):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Berg&#x27;s Little Printer (not live anymore)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;&quot;&gt;Comparing the weather of places I&#x27;ve lived in&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;My experience of using NixOps as an Ansible user (not live anymore)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;trying-to-replace-json-with-protobuf&#x2F;&quot;&gt;Using Protobuf instead of JSON to communicate with a frontend&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;We always tweet when we post an article but only post to HN&#x2F;Reddit the ones we feel are interesting enough. Article 1 has not been posted anywhere, number 2 has been posted on &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;python&quot;&gt;r&#x2F;python&lt;&#x2F;a&gt; and number 3 and 4 were both on the front page of HackerNews, with number 4 being at the top for some time.
From that graph and my experience with my &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;&quot;&gt;previous blog&lt;&#x2F;a&gt;, HN brings way more readers than reddit or twitter.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s have a look to see if I&#x27;m right.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;channels &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;channels.csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# We need to ensure we only have ints
&lt;&#x2F;span&gt;&lt;span&gt;channels[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;intify_sessions&lt;&#x2F;span&gt;&lt;span&gt;(channels)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;wedges &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;channels[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;pie&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;autopct&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%1.1f%%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;channels[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Default Channel Grouping&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labeldistance&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.1
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;axis&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;equal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_3_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_3_0.png&quot; alt=&quot;GA stats: source&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We can see most of our sessions are coming from Social, which includes HN&#x2F;Reddit&#x2F;Twitter mainly: 16,946 to be exact.
We also have a significant portion of users coming from referrals which are mainly Python newsletters, &lt;a href=&quot;http:&#x2F;&#x2F;feedly.com&quot;&gt;feedly.com&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;lobste.rs&quot;&gt;lobste.rs&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;hckrnews.com&quot;&gt;hckrnews.com&lt;&#x2F;a&gt; and &lt;a href=&quot;http:&#x2F;&#x2F;flipboard.com&quot;&gt;flipboard.com&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s drill down into those social sessions to see where they are coming from but since I am lazy (and we don&#x27;t want a pie chart overdose!) I will just paste the number of sessions from each rather having a pie chart for that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hacker News&lt;&#x2F;strong&gt;: 15,223 (89.83%)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Twitter&lt;&#x2F;strong&gt;: 1,008 (5.95%)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reddit&lt;&#x2F;strong&gt;: 323 (1.91%)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Facebook&lt;&#x2F;strong&gt;: 247 (1.46%)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Other&lt;&#x2F;strong&gt;: 144 (0.85%)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I was a bit surprised by the Twitter amount so I went and found that there is a HN bot with lots of followers that tweets the articles. I guess most of them come from that tweet since we are not really active on Twitter ourselves.&lt;&#x2F;p&gt;
&lt;p&gt;This is cool but I am more interested on what location&#x2F;devices the people are using so let&#x27;s have a look.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;locations &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;locations.csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;locations[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;intify_sessions&lt;&#x2F;span&gt;&lt;span&gt;(locations)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;ax &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;locations[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;barh&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;ax.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;set_yticklabels&lt;&#x2F;span&gt;&lt;span&gt;(locations[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Country&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;ax.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;invert_yaxis&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;tick_params&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;axis&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;both&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;which&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;major&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labelsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Number of sessions by countries (top 10)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;ax.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;set_xlabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Number of sessions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_5_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_5_0.png&quot; alt=&quot;GA stats: countries&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;As expected, most of the users are from English speaking countries, the articles being written in English after all. We could translate them into French&#x2F;German if we wanted but that wouldn&#x27;t be worth the effort of maintaining multiple versions of each article.&lt;&#x2F;p&gt;
&lt;p&gt;In terms of cities, the top 3 are &lt;em&gt;San Francisco&lt;&#x2F;em&gt;, &lt;em&gt;New York&lt;&#x2F;em&gt; and &lt;em&gt;London&lt;&#x2F;em&gt;. Again nothing surprising.  The first non english-speaking city is &lt;em&gt;Berlin&lt;&#x2F;em&gt; at the 8th position but &lt;em&gt;Paris&lt;&#x2F;em&gt; is only 4 sessions away from overtaking &lt;em&gt;Los Angeles&lt;&#x2F;em&gt; and taking the 10th position.&lt;&#x2F;p&gt;
&lt;p&gt;To me, having people in thousands of cities in over 100 countries reading what we write is pretty mind-blowing and is by far the best part.&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t finish that notebook without satisfying some of my curiosity: what devices&#x2F;OS&#x2F;browsers are used?&lt;&#x2F;p&gt;
&lt;p&gt;First, what devices are readers mostly using?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;desktop&lt;&#x2F;strong&gt;: 16,211 (64.94%)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;mobile&lt;&#x2F;strong&gt;: 7,335 (29.38%)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;tablet&lt;&#x2F;strong&gt;: 1,418 (5.68%)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Nothing surprising here either, most people browse websites from a computer. In the mobile&#x2F;tablet category, iPhone and iPad are the big winners in terms of number of users but Android still wins due to the sheer number of devices as we will see below.&lt;&#x2F;p&gt;
&lt;p&gt;If anyone is working on a B2B app, would you be able to share those numbers? It seems to me that you could forego responsiveness for quite a lot of B2B products, or only have a basic one.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;oses &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;os.csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;oses[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;intify_sessions&lt;&#x2F;span&gt;&lt;span&gt;(oses)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Limiting to OSes over 200 users, sorry WP, Chrome OS,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Firefox OS and Free&#x2F;OpenBSD users
&lt;&#x2F;span&gt;&lt;span&gt;oses[oses[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;pie&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;autopct&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%1.1f%%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;oses[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Operating System&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labeldistance&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.1
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;axis&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;equal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_8_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_8_0.png&quot; alt=&quot;GA stats: os&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Interestingly enough, Android and iOS are almost identical.&lt;&#x2F;p&gt;
&lt;p&gt;I have to say I&#x27;m a bit surprised by the predominance of Macs, I was expecting it to be at around 25% and have more Linux users than that.
For Windows and Mac, it seems the readers are using majoritarily the last 2 versions of each OS, with 7 being the most used version for Window and 10.10 for Mac.
A special hello for the person that came from Windows Server 2003 and the few still on Vista.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see what browsers everyone is using now.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;browsers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;browsers.csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;browsers[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;intify_sessions&lt;&#x2F;span&gt;&lt;span&gt;(browsers)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Limiting to browsers over 200 users,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# sorry IE&#x2F;Opera&#x2F;Blackberry users
&lt;&#x2F;span&gt;&lt;span&gt;browsers[browsers[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Sessions&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;pie&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;autopct&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%1.1f%%&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;labels&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;browsers[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Browser&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;axis&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;equal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_10_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;looking-at-hackernews-effect&#x2F;ga-stats_10_0.png&quot; alt=&quot;GA stats: browsers&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;This is a pretty damn big win for Chrome here and almost all of those are on Chrome 42 or 43.
Most Safari and Firefox users are also on the latest version of their respective browsers.&lt;&#x2F;p&gt;
&lt;p&gt;About 200 sessions came from IE and they were mostly using IE10 or IE11 so if you are a frontend person and you are targeting the HN crowd, you don&#x27;t have to worry about old browsers really (but I think you already knew that).&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the list below those major players is interesting though, you find out about things like &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Amazon_Silk&quot;&gt;Amazon Silk&lt;&#x2F;a&gt; or &lt;a href=&quot;http:&#x2F;&#x2F;help.yandex.ru&#x2F;yabrowser&#x2F;?lang=en&quot;&gt;YaBrowser&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-server&quot;&gt;The server&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-server&quot; aria-label=&quot;Anchor link for: the-server&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Just a quick word to praise static sites: we ran 15 QPS excluding static assets (50 QPS including those) on a micro instance at 0.0 load with 500 concurrent users.&lt;&#x2F;p&gt;
&lt;p&gt;We use &lt;a href=&quot;http:&#x2F;&#x2F;docs.getpelican.com&#x2F;en&#x2F;3.5.0&#x2F;&quot;&gt;Pelican&lt;&#x2F;a&gt; but any of the dozens of other similar tools is fine and you can handle ridiculous amount of traffic without any worries. You can also host the blog on S3 or a CDN if you don&#x27;t want to have a server at all.
Using a static site generator also makes it easy to review and improve articles, like you would do for code reviews. The only pain point is commenting on notebooks, as it is not possible to do so on the rendered version on Github and a raw notebook looks like &lt;a href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;WeAreWizards&#x2F;blog&#x2F;master&#x2F;content&#x2F;notebooks&#x2F;weather.ipynb&quot;&gt;that&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can check &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WeAreWizards&#x2F;blog&quot;&gt;our blog repo&lt;&#x2F;a&gt; and look at the issues if you are curious about that process.&lt;&#x2F;p&gt;
&lt;p&gt;Now, onto vacations!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using Protobuf instead of JSON to communicate with a frontend</title>
          <pubDate>Sun, 14 Jun 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/trying-to-replace-json-with-protobuf/</link>
          <guid>https://www.vincentprouillet.com/blog/trying-to-replace-json-with-protobuf/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/trying-to-replace-json-with-protobuf/">&lt;p&gt;Protocol buffers (or other binary serialization formats like Thrift) are widely used to communicate between services. JSON is overwhelmingly used for backend &amp;lt;-&amp;gt; frontend communication and for APIs.
Let&#x27;s see what a client&#x2F;server using Protobuf would look like.&lt;&#x2F;p&gt;
&lt;!-- PELICAN_END_SUMMARY --&gt;
&lt;h2 id=&quot;what-is-protobuf&quot;&gt;What is Protobuf&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-protobuf&quot; aria-label=&quot;Anchor link for: what-is-protobuf&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;protocol-buffers&#x2F;docs&#x2F;overview&quot;&gt;Protobuf&lt;&#x2F;a&gt; is a binary serialization format from Google meant to serialize structured data. It has librairies in most languages, including Python and Javascript which we are going to use in our toy application.&lt;&#x2F;p&gt;
&lt;p&gt;Tools like Protobuf have a few advantages over JSON:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;smaller in size&lt;&#x2F;li&gt;
&lt;li&gt;typed&lt;&#x2F;li&gt;
&lt;li&gt;offer a common interface for all your services, you can just update your .proto or .thrift and share those with the services using it, as long as there is a library for that language&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;thrift.apache.org&#x2F;&quot;&gt;Thrift&lt;&#x2F;a&gt; does more as it also allows you to define methods as well as structs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-app&quot;&gt;Example app&lt;a class=&quot;zola-anchor&quot; href=&quot;#example-app&quot; aria-label=&quot;Anchor link for: example-app&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As an example we are going to make a small address book app. Coincidentally that&#x27;s the example that the Python Protobuf documention is using as well but that wasn&#x27;t on purpose, I swear! The data is generated randomly on startup of the flask app.
The repo is in our &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WeAreWizards&#x2F;protojson&quot;&gt;github&lt;&#x2F;a&gt; if you want to try it yourself.
As mentioned in the README, the Python library for Protobuf does not support Python 3 (yet) so you will have to make a Python 2 virtualenv if you decide to install the server.
The server will deliver the content in JSON or Protobuf based on the &lt;code&gt;Accept&lt;&#x2F;code&gt; header.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;server-side&quot;&gt;Server-side&lt;a class=&quot;zola-anchor&quot; href=&quot;#server-side&quot; aria-label=&quot;Anchor link for: server-side&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s have a quick look at the server, it might be familiar to you if you have used Protobuf before but it is helpful for the rest of us (like me, I only used Thrift before).
First we need to define our Protobuf schema, which is done in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WeAreWizards&#x2F;protojson&#x2F;blob&#x2F;master&#x2F;addressbook.proto&quot;&gt;addressbook.proto&lt;&#x2F;a&gt; file. Here is one of the model as example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;protobuf&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-protobuf &quot;&gt;&lt;code class=&quot;language-protobuf&quot; data-lang=&quot;protobuf&quot;&gt;&lt;span&gt;message Contact {
&lt;&#x2F;span&gt;&lt;span&gt;  required string first_name = 1;
&lt;&#x2F;span&gt;&lt;span&gt;  required string last_name = 2;
&lt;&#x2F;span&gt;&lt;span&gt;  optional Address address = 3;
&lt;&#x2F;span&gt;&lt;span&gt;  repeated Phone phone_numbers = 4;
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this model, &lt;code&gt;first_name&lt;&#x2F;code&gt; and &lt;code&gt;last_name&lt;&#x2F;code&gt; are required (controversial, I&#x27;ll come back to that later), the address model is optional and we can have 0, 1 or n &lt;code&gt;phone_numbers&lt;&#x2F;code&gt;.
Now, Python doesn&#x27;t know about .proto files so we need to use the &lt;a href=&quot;https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;protobuf&quot;&gt;Python library&lt;&#x2F;a&gt; to generate code that we can use. This is the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WeAreWizards&#x2F;protojson&#x2F;blob&#x2F;master&#x2F;addressbook_pb2.py&quot;&gt;addressbook_pb2.py&lt;&#x2F;a&gt; file. You can then import that in your Python code and access the models defined in your .proto file.
Have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WeAreWizards&#x2F;protojson&#x2F;blob&#x2F;master&#x2F;data.py&quot;&gt;data.py&lt;&#x2F;a&gt; file to see an example. You can use those models as you would with a class and using kwargs for every attributes.
You also need to serialize them before sending, using the &lt;code&gt;SerializeToString()&lt;&#x2F;code&gt; method in our app.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;client-side&quot;&gt;Client-side&lt;a class=&quot;zola-anchor&quot; href=&quot;#client-side&quot; aria-label=&quot;Anchor link for: client-side&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The client is written in Angular purely as an example and because ng-repeat was exactly what I wanted here. The code is a quick prototype so don&#x27;t expect it to be idiomatic.
It uses &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dcodeIO&#x2F;ProtoBuf.js&quot;&gt;ProtoBuf.js&lt;&#x2F;a&gt; as a Protobuf library.
The first thing we do is a basic GET to get all our contacts which is trivial since you can just dump the response data into our scope variable. On the other hand, we need to handle Protobuf deserialization since looking at our network tab we receive binary data looking like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;�Marlon��Sheets��
&lt;&#x2F;span&gt;&lt;span&gt;�7771 Eastern Avenue��11239
&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;�Harley��Malloy����60139&amp;quot;����
&lt;&#x2F;span&gt;&lt;span&gt;(712)614-9303
&lt;&#x2F;span&gt;&lt;span&gt;Y
&lt;&#x2F;span&gt;&lt;span&gt;�Mack�  Hendricks�3
&lt;&#x2F;span&gt;&lt;span&gt;�9573 Kimridge Cove
&lt;&#x2F;span&gt;&lt;span&gt;�7490 Orchard Hill Cove��11239&amp;quot;����
&lt;&#x2F;span&gt;&lt;span&gt;(802)466-7004
&lt;&#x2F;span&gt;&lt;span&gt;�
&lt;&#x2F;span&gt;&lt;span&gt;�Madeline��Bowen����87023
&lt;&#x2F;span&gt;&lt;span&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;�Willa��Sadler��
&lt;&#x2F;span&gt;&lt;span&gt;�693 Burlington Parkway��43824
&lt;&#x2F;span&gt;&lt;span&gt;T
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(Note that I had to replace some of the characters so the RSS could be generated).
By default, this is more compact than JSON by quite a bit but once I turned on GZIP, the difference became much smaller: 851B for ProtoBuf and 942B for JSON.&lt;&#x2F;p&gt;
&lt;p&gt;In the example I copied the .proto file into the static directory and created our &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WeAreWizards&#x2F;protojson&#x2F;blob&#x2F;master&#x2F;static&#x2F;main.js#L12-L15&quot;&gt;models&lt;&#x2F;a&gt; following the ProtoBuf.js tutorial.
Now the only two things left to do for our GET is to add a &lt;code&gt;responseType: &#x27;arraybuffer&#x27;&lt;&#x2F;code&gt; in our HTTP requests (which would be done in the config for a proper angular app) and decode the data we receive using &lt;code&gt;MyModel.decode(data)&lt;&#x2F;code&gt;.
Creating an instance looks like a class in JavaScript as well:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;contact &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Contact&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;  first_name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_contact&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;firstName&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;  last_name: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_contact&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;lastName&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that supplying an incorrect parameter such as an unknown field will result in a runtime error.&lt;&#x2F;p&gt;
&lt;p&gt;POSTing is a bit more complex as Angular tries to be too clever and forces us to do some changes in the HTTP request:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; The transformRequest is needed
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;req &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  method: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;POST&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  url: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&#x2F;api&#x2F;contacts&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  responseType: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;arraybuffer&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;transformRequest&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;) { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;;},
&lt;&#x2F;span&gt;&lt;span&gt;  data: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;contact&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;toArrayBuffer&lt;&#x2F;span&gt;&lt;span&gt;(),
&lt;&#x2F;span&gt;&lt;span&gt;  headers: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;binary&#x2F;octet-stream&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While I see the need for Protobuf and Thrift for services communication, I don&#x27;t really see the point of using it instead of JSON for the frontend.
First, the network tab becomes pretty much unusable and I use it very often.
Since Protobuf doesn&#x27;t use setters, you can use wrong types in your object and it will only throw an error much later, but earlier than with vanilla JavaScript. Using &lt;a href=&quot;http:&#x2F;&#x2F;www.typescriptlang.org&#x2F;&quot;&gt;Typescript&lt;&#x2F;a&gt; or &lt;a href=&quot;http:&#x2F;&#x2F;flowtype.org&#x2F;&quot;&gt;Flow&lt;&#x2F;a&gt; with good definitions would solve many issues at compile time.
Another point I raised earlier is that Google recommends to mark every field as optional in Protobuf, which is better in terms of compatibility but means you can send incomplete data.
Has any of you used a binary serialization for that successfully? Let us know in the comments or send us a tweet &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;WeAreWizardsIO&quot;&gt;@WeAreWizardsIO&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Comparing weathers in places I&#x27;ve lived</title>
          <pubDate>Fri, 22 May 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/comparing-weathers/</link>
          <guid>https://www.vincentprouillet.com/blog/comparing-weathers/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/comparing-weathers/">&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;matplotlib inline
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;datetime
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;matplotlib.pyplot &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;plt
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;numpy &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;np
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;pandas &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;pd
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;seaborn  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# make charts prettier and more readable
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Let&amp;#39;s load one of the CSV to see what they look like
&lt;&#x2F;span&gt;&lt;span&gt;nice &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;nice.csv&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;nice[:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;table border=&quot;1&quot; class=&quot;dataframe&quot;&gt;
  &lt;thead&gt;
    &lt;tr style=&quot;text-align: right;&quot;&gt;
      &lt;th&gt;&lt;&#x2F;th&gt;
      &lt;th&gt;month&lt;&#x2F;th&gt;
      &lt;th&gt;avg_temp&lt;&#x2F;th&gt;
      &lt;th&gt;min_temp&lt;&#x2F;th&gt;
      &lt;th&gt;max_temp&lt;&#x2F;th&gt;
      &lt;th&gt;humidity&lt;&#x2F;th&gt;
      &lt;th&gt;rainfall&lt;&#x2F;th&gt;
      &lt;th&gt;raindays&lt;&#x2F;th&gt;
      &lt;th&gt;snowdays&lt;&#x2F;th&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;&#x2F;th&gt;
      &lt;td&gt;01-2011&lt;&#x2F;td&gt;
      &lt;td&gt;8.6&lt;&#x2F;td&gt;
      &lt;td&gt;4.8&lt;&#x2F;td&gt;
      &lt;td&gt;12.4&lt;&#x2F;td&gt;
      &lt;td&gt;67.0&lt;&#x2F;td&gt;
      &lt;td&gt;2.9&lt;&#x2F;td&gt;
      &lt;td&gt;5&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;&#x2F;th&gt;
      &lt;td&gt;02-2011&lt;&#x2F;td&gt;
      &lt;td&gt;9.3&lt;&#x2F;td&gt;
      &lt;td&gt;7.1&lt;&#x2F;td&gt;
      &lt;td&gt;11.1&lt;&#x2F;td&gt;
      &lt;td&gt;69.1&lt;&#x2F;td&gt;
      &lt;td&gt;2.9&lt;&#x2F;td&gt;
      &lt;td&gt;5&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;&#x2F;th&gt;
      &lt;td&gt;03-2011&lt;&#x2F;td&gt;
      &lt;td&gt;11.7&lt;&#x2F;td&gt;
      &lt;td&gt;7.8&lt;&#x2F;td&gt;
      &lt;td&gt;14.3&lt;&#x2F;td&gt;
      &lt;td&gt;67.0&lt;&#x2F;td&gt;
      &lt;td&gt;3.9&lt;&#x2F;td&gt;
      &lt;td&gt;5&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;&#x2F;th&gt;
      &lt;td&gt;04-2011&lt;&#x2F;td&gt;
      &lt;td&gt;15.6&lt;&#x2F;td&gt;
      &lt;td&gt;13.3&lt;&#x2F;td&gt;
      &lt;td&gt;18.5&lt;&#x2F;td&gt;
      &lt;td&gt;67.4&lt;&#x2F;td&gt;
      &lt;td&gt;0.4&lt;&#x2F;td&gt;
      &lt;td&gt;1&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;&#x2F;th&gt;
      &lt;td&gt;05-2011&lt;&#x2F;td&gt;
      &lt;td&gt;19.7&lt;&#x2F;td&gt;
      &lt;td&gt;16.6&lt;&#x2F;td&gt;
      &lt;td&gt;24.8&lt;&#x2F;td&gt;
      &lt;td&gt;60.6&lt;&#x2F;td&gt;
      &lt;td&gt;0.0&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;We can see the shape of our CSV files.
There are 8 columns, all pretty explicit and containing the average for the month in column month except for raindays and snowdays which contains the number of days in that month where it rained and snowed respectively.&lt;&#x2F;p&gt;
&lt;p&gt;Note: I currently count raindays as the number of days where the rainfall is above 5mm (which is completely arbitrary).
If you look at the website I scraped it does contain a column for rain days  but some days are marked as rain days when there is no rainfall and the reverse.
Even then it&#x27;s not really accurate as I&#x27;m only interested in quality of life and rain at 4am is not annoying lots of people.&lt;&#x2F;p&gt;
&lt;p&gt;Cool. Let&#x27;s try plotting something first to see if everything is okay and load the other cities.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;nice[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;avg_temp&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_3_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_3_0.png&quot; alt=&quot;Average temperature in Nice&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;While we could continue having one &lt;code&gt;DataFrame&lt;&#x2F;code&gt; per city, it is more convenient to have one &lt;code&gt;DataFrame&lt;&#x2F;code&gt; containing all the data as this allows us to use plotting directly from it like we did above.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;locations &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;nice&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;montreal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;okinawa&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;london&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;weather &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;DataFrame&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;location &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;locations:
&lt;&#x2F;span&gt;&lt;span&gt;    frame &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read_csv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;.csv&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;location)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# We need to keep track of where it&amp;#39;s coming from obviously
&lt;&#x2F;span&gt;&lt;span&gt;    frame[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;location&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;location
&lt;&#x2F;span&gt;&lt;span&gt;    weather &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;weather.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(frame)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Alternative to using slicing
&lt;&#x2F;span&gt;&lt;span&gt;weather.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;contained&quot; style=&quot;max-height:1000px;overflow:auto;&quot;&gt;
&lt;table border=&quot;1&quot; class=&quot;dataframe&quot;&gt;
  &lt;thead&gt;
    &lt;tr style=&quot;text-align: right;&quot;&gt;
      &lt;th&gt;&lt;&#x2F;th&gt;
      &lt;th&gt;month&lt;&#x2F;th&gt;
      &lt;th&gt;avg_temp&lt;&#x2F;th&gt;
      &lt;th&gt;min_temp&lt;&#x2F;th&gt;
      &lt;th&gt;max_temp&lt;&#x2F;th&gt;
      &lt;th&gt;humidity&lt;&#x2F;th&gt;
      &lt;th&gt;rainfall&lt;&#x2F;th&gt;
      &lt;th&gt;raindays&lt;&#x2F;th&gt;
      &lt;th&gt;snowdays&lt;&#x2F;th&gt;
      &lt;th&gt;location&lt;&#x2F;th&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;0&lt;&#x2F;th&gt;
      &lt;td&gt;01-2011&lt;&#x2F;td&gt;
      &lt;td&gt;8.6&lt;&#x2F;td&gt;
      &lt;td&gt;4.8&lt;&#x2F;td&gt;
      &lt;td&gt;12.4&lt;&#x2F;td&gt;
      &lt;td&gt;67.0&lt;&#x2F;td&gt;
      &lt;td&gt;2.9&lt;&#x2F;td&gt;
      &lt;td&gt;5&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;1&lt;&#x2F;th&gt;
      &lt;td&gt;02-2011&lt;&#x2F;td&gt;
      &lt;td&gt;9.3&lt;&#x2F;td&gt;
      &lt;td&gt;7.1&lt;&#x2F;td&gt;
      &lt;td&gt;11.1&lt;&#x2F;td&gt;
      &lt;td&gt;69.1&lt;&#x2F;td&gt;
      &lt;td&gt;2.9&lt;&#x2F;td&gt;
      &lt;td&gt;5&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;2&lt;&#x2F;th&gt;
      &lt;td&gt;03-2011&lt;&#x2F;td&gt;
      &lt;td&gt;11.7&lt;&#x2F;td&gt;
      &lt;td&gt;7.8&lt;&#x2F;td&gt;
      &lt;td&gt;14.3&lt;&#x2F;td&gt;
      &lt;td&gt;67.0&lt;&#x2F;td&gt;
      &lt;td&gt;3.9&lt;&#x2F;td&gt;
      &lt;td&gt;5&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;3&lt;&#x2F;th&gt;
      &lt;td&gt;04-2011&lt;&#x2F;td&gt;
      &lt;td&gt;15.6&lt;&#x2F;td&gt;
      &lt;td&gt;13.3&lt;&#x2F;td&gt;
      &lt;td&gt;18.5&lt;&#x2F;td&gt;
      &lt;td&gt;67.4&lt;&#x2F;td&gt;
      &lt;td&gt;0.4&lt;&#x2F;td&gt;
      &lt;td&gt;1&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;4&lt;&#x2F;th&gt;
      &lt;td&gt;05-2011&lt;&#x2F;td&gt;
      &lt;td&gt;19.7&lt;&#x2F;td&gt;
      &lt;td&gt;16.6&lt;&#x2F;td&gt;
      &lt;td&gt;24.8&lt;&#x2F;td&gt;
      &lt;td&gt;60.6&lt;&#x2F;td&gt;
      &lt;td&gt;0.0&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We can also use &lt;code&gt;.describe()&lt;&#x2F;code&gt; on a &lt;code&gt;DataFrame&lt;&#x2F;code&gt; instead of doing &lt;code&gt;.median()&lt;&#x2F;code&gt;, &lt;code&gt;.sum()&lt;&#x2F;code&gt; etc on every single column to have a quick overview of all the variables.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;weather.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;describe&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;contained&quot; style=&quot;max-height:1000px;overflow:auto;&quot;&gt;
&lt;table border=&quot;1&quot; class=&quot;dataframe&quot;&gt;
  &lt;thead&gt;
    &lt;tr style=&quot;text-align: right;&quot;&gt;
      &lt;th&gt;&lt;&#x2F;th&gt;
      &lt;th&gt;avg_temp&lt;&#x2F;th&gt;
      &lt;th&gt;min_temp&lt;&#x2F;th&gt;
      &lt;th&gt;max_temp&lt;&#x2F;th&gt;
      &lt;th&gt;humidity&lt;&#x2F;th&gt;
      &lt;th&gt;rainfall&lt;&#x2F;th&gt;
      &lt;th&gt;raindays&lt;&#x2F;th&gt;
      &lt;th&gt;snowdays&lt;&#x2F;th&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;count&lt;&#x2F;th&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
      &lt;td&gt;192.000000&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;mean&lt;&#x2F;th&gt;
      &lt;td&gt;14.698437&lt;&#x2F;td&gt;
      &lt;td&gt;9.718750&lt;&#x2F;td&gt;
      &lt;td&gt;19.635937&lt;&#x2F;td&gt;
      &lt;td&gt;70.260937&lt;&#x2F;td&gt;
      &lt;td&gt;3.507292&lt;&#x2F;td&gt;
      &lt;td&gt;4.843750&lt;&#x2F;td&gt;
      &lt;td&gt;2.114583&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;std&lt;&#x2F;th&gt;
      &lt;td&gt;9.001405&lt;&#x2F;td&gt;
      &lt;td&gt;11.141769&lt;&#x2F;td&gt;
      &lt;td&gt;7.269971&lt;&#x2F;td&gt;
      &lt;td&gt;7.409749&lt;&#x2F;td&gt;
      &lt;td&gt;3.491553&lt;&#x2F;td&gt;
      &lt;td&gt;3.241834&lt;&#x2F;td&gt;
      &lt;td&gt;5.640857&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;min&lt;&#x2F;th&gt;
      &lt;td&gt;-10.000000&lt;&#x2F;td&gt;
      &lt;td&gt;-24.900000&lt;&#x2F;td&gt;
      &lt;td&gt;1.600000&lt;&#x2F;td&gt;
      &lt;td&gt;54.200000&lt;&#x2F;td&gt;
      &lt;td&gt;0.000000&lt;&#x2F;td&gt;
      &lt;td&gt;0.000000&lt;&#x2F;td&gt;
      &lt;td&gt;0.000000&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;25%&lt;&#x2F;th&gt;
      &lt;td&gt;9.150000&lt;&#x2F;td&gt;
      &lt;td&gt;4.650000&lt;&#x2F;td&gt;
      &lt;td&gt;13.950000&lt;&#x2F;td&gt;
      &lt;td&gt;65.300000&lt;&#x2F;td&gt;
      &lt;td&gt;1.400000&lt;&#x2F;td&gt;
      &lt;td&gt;2.750000&lt;&#x2F;td&gt;
      &lt;td&gt;0.000000&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;50%&lt;&#x2F;th&gt;
      &lt;td&gt;16.250000&lt;&#x2F;td&gt;
      &lt;td&gt;11.950000&lt;&#x2F;td&gt;
      &lt;td&gt;20.950000&lt;&#x2F;td&gt;
      &lt;td&gt;69.750000&lt;&#x2F;td&gt;
      &lt;td&gt;2.700000&lt;&#x2F;td&gt;
      &lt;td&gt;5.000000&lt;&#x2F;td&gt;
      &lt;td&gt;0.000000&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;75%&lt;&#x2F;th&gt;
      &lt;td&gt;21.025000&lt;&#x2F;td&gt;
      &lt;td&gt;16.600000&lt;&#x2F;td&gt;
      &lt;td&gt;25.225000&lt;&#x2F;td&gt;
      &lt;td&gt;74.800000&lt;&#x2F;td&gt;
      &lt;td&gt;3.900000&lt;&#x2F;td&gt;
      &lt;td&gt;7.000000&lt;&#x2F;td&gt;
      &lt;td&gt;0.000000&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;max&lt;&#x2F;th&gt;
      &lt;td&gt;29.600000&lt;&#x2F;td&gt;
      &lt;td&gt;28.400000&lt;&#x2F;td&gt;
      &lt;td&gt;30.700000&lt;&#x2F;td&gt;
      &lt;td&gt;87.500000&lt;&#x2F;td&gt;
      &lt;td&gt;22.000000&lt;&#x2F;td&gt;
      &lt;td&gt;15.000000&lt;&#x2F;td&gt;
      &lt;td&gt;25.000000&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Define some styles that we will reuse for all line graphs
&lt;&#x2F;span&gt;&lt;span&gt;styles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;london&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;go-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;nice&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;ro-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;montreal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;bo-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;okinawa&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;co-&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# we define a method since we will need to do that pretty often
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;plot_grouped_by&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;dataframe&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;column_name&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Plots the dataframe grouped by location for the given column&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Need to use the month as the index
&lt;&#x2F;span&gt;&lt;span&gt;    locations &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;dataframe.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;set_index&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;month&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;groupby&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;location&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;loc_name, loc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;locations:
&lt;&#x2F;span&gt;&lt;span&gt;        loc[column_name].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;month&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;label&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;(loc_name), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;styles[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;(loc_name)])
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;ax &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;111&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot_grouped_by&lt;&#x2F;span&gt;&lt;span&gt;(weather, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;avg_temp&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Yes, I did add the 40 degrees tick just to be able to fit the legend properly
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;yticks&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;legend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;upper left&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Monthly average temperature 2011-2014&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Temperature (celsius)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;xlabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Time&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_8_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_8_0.png&quot; alt=&quot;Monthly average temperatures&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We can make a few observations on this chart:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Montreal has the biggest variance in temperature throughout the year: from very cold in winter to quite warm in summer&lt;&#x2F;li&gt;
&lt;li&gt;London is disappointingly average and is missing at least 10° on its summer time for my taste&lt;&#x2F;li&gt;
&lt;li&gt;Okinawa is hot, even winter is warmer than London&#x27;s summer most of the time&lt;&#x2F;li&gt;
&lt;li&gt;Nice has a nice weather, hot in summer but not too cold in winter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For the next plots, let&#x27;s focus on 2014 only in order to have an idea on how a year looks like in those cities.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Making sure we have a datetime first rather than a string
&lt;&#x2F;span&gt;&lt;span&gt;weather[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;month&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;pd.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;to_datetime&lt;&#x2F;span&gt;&lt;span&gt;(weather[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;month&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%Y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;start &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;datetime.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2014&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# pandas allows all kind of iterator magic
&lt;&#x2F;span&gt;&lt;span&gt;weather_2014 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;weather[weather.month &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;= &lt;&#x2F;span&gt;&lt;span&gt;start]
&lt;&#x2F;span&gt;&lt;span&gt;weather_2014.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;head&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;contained&quot; style=&quot;max-height:1000px;overflow:auto;&quot;&gt;
&lt;table border=&quot;1&quot; class=&quot;dataframe&quot;&gt;
  &lt;thead&gt;
    &lt;tr style=&quot;text-align: right;&quot;&gt;
      &lt;th&gt;&lt;&#x2F;th&gt;
      &lt;th&gt;month&lt;&#x2F;th&gt;
      &lt;th&gt;avg_temp&lt;&#x2F;th&gt;
      &lt;th&gt;min_temp&lt;&#x2F;th&gt;
      &lt;th&gt;max_temp&lt;&#x2F;th&gt;
      &lt;th&gt;humidity&lt;&#x2F;th&gt;
      &lt;th&gt;rainfall&lt;&#x2F;th&gt;
      &lt;th&gt;raindays&lt;&#x2F;th&gt;
      &lt;th&gt;snowdays&lt;&#x2F;th&gt;
      &lt;th&gt;location&lt;&#x2F;th&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;36&lt;&#x2F;th&gt;
      &lt;td&gt;2014-01-01&lt;&#x2F;td&gt;
      &lt;td&gt;9.7&lt;&#x2F;td&gt;
      &lt;td&gt;6.4&lt;&#x2F;td&gt;
      &lt;td&gt;12.4&lt;&#x2F;td&gt;
      &lt;td&gt;71.6&lt;&#x2F;td&gt;
      &lt;td&gt;9.2&lt;&#x2F;td&gt;
      &lt;td&gt;11&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;37&lt;&#x2F;th&gt;
      &lt;td&gt;2014-02-01&lt;&#x2F;td&gt;
      &lt;td&gt;9.9&lt;&#x2F;td&gt;
      &lt;td&gt;6.8&lt;&#x2F;td&gt;
      &lt;td&gt;13.4&lt;&#x2F;td&gt;
      &lt;td&gt;69.6&lt;&#x2F;td&gt;
      &lt;td&gt;4.6&lt;&#x2F;td&gt;
      &lt;td&gt;7&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;38&lt;&#x2F;th&gt;
      &lt;td&gt;2014-03-01&lt;&#x2F;td&gt;
      &lt;td&gt;12.5&lt;&#x2F;td&gt;
      &lt;td&gt;7.4&lt;&#x2F;td&gt;
      &lt;td&gt;15.4&lt;&#x2F;td&gt;
      &lt;td&gt;62.3&lt;&#x2F;td&gt;
      &lt;td&gt;2.7&lt;&#x2F;td&gt;
      &lt;td&gt;3&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;39&lt;&#x2F;th&gt;
      &lt;td&gt;2014-04-01&lt;&#x2F;td&gt;
      &lt;td&gt;15.3&lt;&#x2F;td&gt;
      &lt;td&gt;13.2&lt;&#x2F;td&gt;
      &lt;td&gt;17.3&lt;&#x2F;td&gt;
      &lt;td&gt;69.3&lt;&#x2F;td&gt;
      &lt;td&gt;0.3&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
    &lt;tr&gt;
      &lt;th&gt;40&lt;&#x2F;th&gt;
      &lt;td&gt;2014-05-01&lt;&#x2F;td&gt;
      &lt;td&gt;17.5&lt;&#x2F;td&gt;
      &lt;td&gt;14.4&lt;&#x2F;td&gt;
      &lt;td&gt;20.2&lt;&#x2F;td&gt;
      &lt;td&gt;64.1&lt;&#x2F;td&gt;
      &lt;td&gt;0.6&lt;&#x2F;td&gt;
      &lt;td&gt;1&lt;&#x2F;td&gt;
      &lt;td&gt;0&lt;&#x2F;td&gt;
      &lt;td&gt;nice&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;tbody&gt;
&lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Let&amp;#39;s look at temperatures and humidity
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# this 221 means we want a 2x2 plots display and this is the first one
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# (so upper left)
&lt;&#x2F;span&gt;&lt;span&gt;ax &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;221&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot_grouped_by&lt;&#x2F;span&gt;&lt;span&gt;(weather_2014, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;avg_temp&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;yticks&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;35&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;legend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;lower center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Monthly average temperature 2014&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Temperature (celsius)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ax2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;222&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot_grouped_by&lt;&#x2F;span&gt;&lt;span&gt;(weather_2014, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;max_temp&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;yticks&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;legend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;lower center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Max temperature 2014&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Temperature (celsius)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ax3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;223&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot_grouped_by&lt;&#x2F;span&gt;&lt;span&gt;(weather_2014, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;min_temp&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;yticks&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;legend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;lower center&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Min temperature 2014&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Temperature (celsius)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ax4 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;224&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot_grouped_by&lt;&#x2F;span&gt;&lt;span&gt;(weather_2014, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;humidity&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Average monthly humidity &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;% i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;n 2014 (legend identical)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Humidity %&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_11_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_11_0.png&quot; alt=&quot;Graph comparing 4 cities&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Looking at those graphs we can notice a few things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Montreal winter is pretty damn cold but is not really humid, which makes it not THAT bad and Canadians know how to do proper insulation. Summer is quite nice all around, nice temperatures and pretty dry.&lt;&#x2F;li&gt;
&lt;li&gt;Nice is good all year round, it sometimes gets below 10° but never reaches 0° while being dry. Having grown up there I feel like I have been spoiled when it comes to weather.&lt;&#x2F;li&gt;
&lt;li&gt;London is average but we can see the winter is very humid, which is the reason why I feel colder when it&#x27;s 0° in London than -15° in Montreal.&lt;&#x2F;li&gt;
&lt;li&gt;Okinawa looks really good until you experience that summer humidity. For those that haven&#x27;t lived in a tropical climate, it means that you are sweating an awful lot very quickly and air con is a necessity.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To finish this notebook, let&#x27;s have a look at the rain and snow data.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;colors &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;london&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;green&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;nice&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;red&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;montreal&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;blue&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;okinawa&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;cyan&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;bar_plot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;column_name&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    weather_2014.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;set_index&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;month&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;location&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;    ).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;unstack&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plot&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ax&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;ax,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;bar&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;column_name
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;figsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;ax &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;211&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;bar_plot&lt;&#x2F;span&gt;&lt;span&gt;(ax, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;raindays&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;legend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;best&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Rain days per month in 2014&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Number of rain days&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ax2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subplot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;212&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;bar_plot&lt;&#x2F;span&gt;&lt;span&gt;(ax2, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;snowdays&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;legend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;loc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;best&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Snow days per month in 2014&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;plt.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ylabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Number of snow days&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fontsize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_13_0.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;comparing-weathers&#x2F;weather_13_0.png&quot; alt=&quot;Rain and snow days&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We can see it&#x27;s raining quite a bit in Okinawa since they have a rainy season (May-June) and a typhoon season (June-November).
Nice is probably the nicest, having close to no rains during summer.
When it comes to snow, Montreal is the only contender as snow in Nice and London is very rare and it hasn&#x27;t snowed in Okinawa for over 30 years.&lt;&#x2F;p&gt;
&lt;p&gt;While weather is only one of the elements that are necessary to consider when moving (along with salary, atmosphere, friends&#x2F;family etc.), it is one of the most important thing for me.
If I had to make a ranking of those cities on a weather basis, it would be:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Nice&lt;&#x2F;li&gt;
&lt;li&gt;Montreal&lt;&#x2F;li&gt;
&lt;li&gt;Okinawa&#x2F;London&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Okinawa and London are tied for completely different reasons: one has the legendary British weather and its 2 weeks of summer and the other is so hot and humid that it can be hard to breath at times (but you get amazing sea and beaches).
Now that you have seen how to use Pandas (for those that had never tried it before), it&#x27;s up to you to finds things to compare. Do share them when you do as it is always interesting to read.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Release of gulp-sass v2</title>
          <pubDate>Thu, 07 May 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/working-on-gulp-sass/</link>
          <guid>https://www.vincentprouillet.com/blog/working-on-gulp-sass/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/working-on-gulp-sass/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dlmanning&#x2F;gulp-sass&quot;&gt;gulp-sass&lt;&#x2F;a&gt; v2 was released today!
In itself, this is not a big change as this is simply a wrapper for node-sass which uses libsass so, while this version is a complete rewrite this is not the important news. The important news is that it ships with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sass&#x2F;node-sass&quot;&gt;node-sass&lt;&#x2F;a&gt; v3 which is using &lt;a href=&quot;http:&#x2F;&#x2F;libsass.org&#x2F;&quot;&gt;libsass&lt;&#x2F;a&gt; 3.2.2.&lt;&#x2F;p&gt;
&lt;p&gt;We will see in a short moment why it is exciting but first a small explanation on what is sass and libsass.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; rm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; -rf&lt;&#x2F;span&gt;&lt;span&gt; node_modules&#x2F;gulp-sass&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npm install gulp-sass&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --save-dev
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; enjoy nearly full sass compatibility
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;sass&quot;&gt;Sass&lt;a class=&quot;zola-anchor&quot; href=&quot;#sass&quot; aria-label=&quot;Anchor link for: sass&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Nowadays, most front-end developers and designers use CSS preprocessors to immensely simplify their work.
A CSS preprocessor brings variables, nesting, mixins, functions and other things to CSS.  Let&#x27;s see a short example of why we would use one, in this case using variables for colours:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;css&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-css &quot;&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.header &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ca7840;&quot;&gt;#484c55&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.header .title &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ca7840;&quot;&gt;#5a5d66&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;* 10% lighter than #484c55 *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.header .title .logo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ca7840;&quot;&gt;#484c55&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And in Sass:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sass&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-sass &quot;&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; functions.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;&#x2F; Slightly lighten a color
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;@function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;tint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$color&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$percentage&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;@return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;mix&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;white&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$color&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$percentage&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; colors.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;@import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;functions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$header-background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ca7840;&quot;&gt;#484c55&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$header-title-background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;tint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$header-background&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ca7840;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; header.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;@import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;colors&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.header &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$header-background&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.title &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$header-title-background&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#9b703f;&quot;&gt;.logo &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c5af75;&quot;&gt;background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$header-background&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The above ignores best practices about naming&#x2F;nesting&#x2F;specificity of selectors for the sake of example.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;While the Sass version is longer, it makes your CSS more readable, easy to organise and consistent. This is only a tiny part of what CSS preprocessors but it hopefully gives you an idea on why they are extremely useful.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;sass-lang.com&#x2F;&quot;&gt;Sass&lt;&#x2F;a&gt; is one of them. There is also &lt;a href=&quot;http:&#x2F;&#x2F;lesscss.org&#x2F;&quot;&gt;less&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;learnboost.github.io&#x2F;stylus&#x2F;&quot;&gt;stylus&lt;&#x2F;a&gt; and a few others but I personally use Sass so I will be talking about that.&lt;&#x2F;p&gt;
&lt;p&gt;Sass is written in Ruby, which means that Ruby is now needed to build your frontend. Ruby is also not the fastest programming language around and the compilation was taking more than 10 seconds for some large projects, which is not really sustainable when working.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;here-comes-a-new-challenger&quot;&gt;Here comes a new challenger&lt;a class=&quot;zola-anchor&quot; href=&quot;#here-comes-a-new-challenger&quot; aria-label=&quot;Anchor link for: here-comes-a-new-challenger&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;akhleung&quot;&gt;Aaron Leung&lt;&#x2F;a&gt; started porting the Sass compiler to C&#x2F;C++.
Lots of people (including me) started playing with it and were impressed by the compilation time an order of magnitude faster than sass but quickly realized that lots of features were not implemented yet, for example &lt;a href=&quot;http:&#x2F;&#x2F;benfrain.com&#x2F;libsass-lightning-fast-sass-compiler-ready-prime-time&#x2F;&quot;&gt;this article&lt;&#x2F;a&gt;. While it was usable for some projects, it could not be a drop-in replacement for most people as you would have to use workarounds for some features and not use some others at all or were using frameworks like Bourbon or earlier versions of Foundation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;HugoGiraudel&quot;&gt;Hugo Giraudel&lt;&#x2F;a&gt; started a website called &lt;a href=&quot;http:&#x2F;&#x2F;sass-compatibility.github.io&#x2F;&quot;&gt;Sass Compatibility&lt;&#x2F;a&gt; to keep track of those incompatibilities.
As mentioned in the introduction, gulp-sass is now using libsass 3.2.2 while it was using 2.0 before and the results of that website look pretty damn good !
Most features are supported except for selector manipulation and string functions, both of which I didn&#x27;t even know existed in the first place.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gulp-sass&quot;&gt;gulp-sass&lt;a class=&quot;zola-anchor&quot; href=&quot;#gulp-sass&quot; aria-label=&quot;Anchor link for: gulp-sass&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned in the introduction, gulp-sass has been rewritten and it means that your existing tasks won&#x27;t work.
Here&#x27;s how to update your gulpfile (from the README):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gulp-sass 1.3.3 task
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;sass&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;scss&#x2F;*.scss&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;sass&lt;&#x2F;span&gt;&lt;span&gt;({errLogToConsole: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;}))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;dest&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;css&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gulp-sass 2.0 task
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;sass&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;scss&#x2F;*.scss&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;sass&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;error&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sass&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;logError&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plugins&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;autoprefixer&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;dest&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;css&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gulp-sass 2.0 synchronous compilation task
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;sass&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;scss&#x2F;*.scss&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sass&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;error&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sass&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;logError&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plugins&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;autoprefixer&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;dest&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;.&#x2F;css&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Any options passed to gulp-sass will be passed down to node-sass, there is no gulp-sass specific options.&lt;&#x2F;p&gt;
&lt;p&gt;As a general advice, I would recommend using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sindresorhus&#x2F;gulp-autoprefixer&quot;&gt;gulp-autoprefixer&lt;&#x2F;a&gt; along as it simplifies your Sass even more since you don&#x27;t have to worry about browsers prefixes and flexbox syntaxes.&lt;&#x2F;p&gt;
&lt;p&gt;There are already issues open on gulp-sass but if you encounter problems (except installations errors, those are most likely node-sass), please open an issue on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dlmanning&#x2F;gulp-sass&#x2F;issues&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;dlmanning&#x2F;gulp-sass&#x2F;issues&lt;&#x2F;a&gt; so we can have a look !
There was over 45k downloads of gulp-sass on npm last week so spread the word about that new version and it might be a good time to try replacing your Ruby Sass!&lt;&#x2F;p&gt;
&lt;p&gt;As an aside, if you are writing Sass I heavily recommend reading those &lt;a href=&quot;http:&#x2F;&#x2F;sass-guidelin.es&#x2F;&quot;&gt;guidelines&lt;&#x2F;a&gt; as they are really good and well detailed.
The only thing missing for me now in the Sass ecosystem is a Ruby-free linter (sounds like a fun Rust learning project).&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Migrating to gulp 4</title>
          <pubDate>Fri, 20 Mar 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/migrating-to-gulp4/</link>
          <guid>https://www.vincentprouillet.com/blog/migrating-to-gulp4/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/migrating-to-gulp4/">&lt;p&gt;Last year I wrote a blog post on my personal website (you can see it here &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;introducing-people-to-gulp&#x2F;&quot;&gt;Gulp by example &lt;&#x2F;a&gt;) showing what &lt;a href=&quot;http:&#x2F;&#x2F;gulpjs.com&#x2F;&quot;&gt;gulp&lt;&#x2F;a&gt; was and making a demo project using it.
Since then, the team behind it did a lot of work on v4 of gulp which, while not released as of the writing of the post, is stable enough to be used.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than going through a list of new features, let&#x27;s update my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&quot;&gt;angular boilerplate&lt;&#x2F;a&gt; to use gulp4.&lt;&#x2F;p&gt;
&lt;p&gt;You can have a look at the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate#user-content-goal&quot;&gt;Goal section of the README&lt;&#x2F;a&gt; to know what this boilerplate does but for those not wanting to open the link: Typescript, Sass, automatic DI, template preloading, testing with Karma and Protractor, live reload with browser-sync.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I&#x27;m not using this boilerplate personally anymore since I don&#x27;t really do vanilla angular anymore.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here&#x27;s the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&#x2F;tree&#x2F;87e75551651e94dfc1aa6135e1ea7cb5bd61cf0f&quot;&gt;starting commit&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updating-dependencies&quot;&gt;Updating dependencies&lt;a class=&quot;zola-anchor&quot; href=&quot;#updating-dependencies&quot; aria-label=&quot;Anchor link for: updating-dependencies&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Nothing fancy here, just updating our npm and bower dependencies, including pointing to the gulp4 branch on github. To use npm install from a github repo, you can use:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npm install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --save-dev&lt;&#x2F;span&gt;&lt;span&gt; gulpjs&#x2F;gulp.git#4.0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The boilerplate demo tests are going to fail at that point because of changes in the api of gulp and probably some in other libraries we just updated as well.&lt;&#x2F;p&gt;
&lt;p&gt;You can see the commit &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&#x2F;commit&#x2F;49a339cc5cbde8f5374b8f4c915a548dc2916cc4&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;migrating-the-gulpfile&quot;&gt;Migrating the gulpfile&lt;a class=&quot;zola-anchor&quot; href=&quot;#migrating-the-gulpfile&quot; aria-label=&quot;Anchor link for: migrating-the-gulpfile&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;calling-gulp&quot;&gt;Calling gulp&lt;a class=&quot;zola-anchor&quot; href=&quot;#calling-gulp&quot; aria-label=&quot;Anchor link for: calling-gulp&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The first thing we need to do is ensure that we are using the right version of gulp.  You might have another version installed globally or nothing installed globally like me.
The easiest way (at least to me) to deal with that issue is the &lt;a href=&quot;https:&#x2F;&#x2F;docs.npmjs.com&#x2F;misc&#x2F;scripts&quot;&gt;npm scripts&lt;&#x2F;a&gt; part of the package.json.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; package.json
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;scripts&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;gulp&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;.&#x2F;node_modules&#x2F;gulp&#x2F;bin&#x2F;gulp.js&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; in your terminal, instead of using gulp, use npm run gulp
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;npm run gulp
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The downside is that you will get npm errors instead of the regular ones but that&#x27;s only an issue when building your gulpfile.
Let&#x27;s not forget to update the travis.yml to use that npm script too.&lt;&#x2F;p&gt;
&lt;p&gt;You can see the commit &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&#x2F;commit&#x2F;ef30315f43580e8b3c1ba85d4536c7fe0d69365d&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fixing-the-errors-we-re-getting&quot;&gt;Fixing the errors we&#x27;re getting&lt;a class=&quot;zola-anchor&quot; href=&quot;#fixing-the-errors-we-re-getting&quot; aria-label=&quot;Anchor link for: fixing-the-errors-we-re-getting&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If you run the gulpfile locally or look at the &lt;a href=&quot;https:&#x2F;&#x2F;travis-ci.org&#x2F;Keats&#x2F;ng-boilerplate&#x2F;builds&#x2F;54252789&quot;&gt;travis build&lt;&#x2F;a&gt;, you will notice we get the following error:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assert.js:87
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;throw&lt;&#x2F;span&gt;&lt;span&gt; new assert.AssertionError({
&lt;&#x2F;span&gt;&lt;span&gt;        ^
&lt;&#x2F;span&gt;&lt;span&gt;AssertionError: Task function must be specified
&lt;&#x2F;span&gt;&lt;span&gt;    at Gulp.set (&#x2F;home&#x2F;vincent&#x2F;Code&#x2F;ng-boilerplate&#x2F;node_modules&#x2F;gulp&#x2F;node_modules&#x2F;undertaker&#x2F;lib&#x2F;set.js:14:3)
&lt;&#x2F;span&gt;&lt;span&gt;    at Gulp.task (&#x2F;home&#x2F;vincent&#x2F;Code&#x2F;ng-boilerplate&#x2F;node_modules&#x2F;gulp&#x2F;node_modules&#x2F;undertaker&#x2F;lib&#x2F;task.js:14:8)
&lt;&#x2F;span&gt;&lt;span&gt;....
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This comes from the fact that gulp.task changed and the 3 parameters signature was removed.
In short, tasks that look like the one below will need to be changed to remove the &lt;code&gt;[]&lt;&#x2F;code&gt; parameter:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;default&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;build&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;runSequence&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;watch&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;karma-watch&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To give a quick background to people not familiar with gulp, the list parameter is a list of tasks that need to be run before running that task.
In this case it will call the build task before running watch and karma-watch in order (runSequence is another thing that can be changed but we will get back to that later).
To solve that very common issue of task dependencies, two methods were added:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gulp.series&lt;&#x2F;code&gt;: will run the tasks in order&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gulp.parallel&lt;&#x2F;code&gt;: will run the tasks in parallel&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s fix our error by replacing those dependent tasks, for example for the default task:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;default&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;series&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;build&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;parallel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;browser-sync&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;watch&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;karma-watch&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In that case we first want to run build, and then browser-sync, watch and karma-watch in parallel.
If you look at the commit below, you will be able to spot an easy mistake: browser-sync is in the &lt;code&gt;gulp.series&lt;&#x2F;code&gt; despite being blocking. In short, don&#x27;t put blocking tasks in a series.&lt;&#x2F;p&gt;
&lt;p&gt;Now trying to run the build task, I get another error:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;09&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;07&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;TypeError&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;undefined &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;is not a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;home&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;vincent&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Code&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;ng&lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;boilerplate&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;node_modules&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;sequence&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;index&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;js&lt;&#x2F;span&gt;&lt;span&gt;:18:22
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;at Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;forEach &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;native&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;at verifyTaskSets &lt;&#x2F;span&gt;&lt;span&gt;(&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;home&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;vincent&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Code&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ng&lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;boilerplate&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;node_modules&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sequence&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;index.js&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since gulp 4 has a method to run things sequentially, let&#x27;s use it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; before
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;build&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(){
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;runSequence&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;clean&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;sass&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;copy-assets&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;ts-compile&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;templates&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;copy-vendor&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;index&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  );
&lt;&#x2F;span&gt;&lt;span&gt;)};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; after
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;task&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;build&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;series&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;clean&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;gulp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;parallel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;sass&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;copy-assets&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;ts-compile&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;templates&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;copy-vendor&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;index&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  )
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I had to modify some tasks like the browser-sync since its API changed.
I also had to make a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dlmanning&#x2F;gulp-sass&#x2F;pull&#x2F;207&quot;&gt;patch&lt;&#x2F;a&gt; to gulp-sass to update the node-sass version.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;You can see the commit &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&#x2F;commit&#x2F;91a05401c6ea532467bc00b5a6e54fd95b6b0eaf&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the &lt;a href=&quot;https:&#x2F;&#x2F;travis-ci.org&#x2F;Keats&#x2F;ng-boilerplate&#x2F;builds&#x2F;54274589&quot;&gt;build&lt;&#x2F;a&gt;, it still fails because of a missing selenium jar which means a protractor error.
My elegant solution was the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&#x2F;commit&#x2F;d999db2442ac72fc440e8ff9cccd8348c533c72d&quot;&gt;following&lt;&#x2F;a&gt;, as in removing protractor from travis because I&#x27;m not using it right now anyway.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;small-cleanup&quot;&gt;Small cleanup&lt;a class=&quot;zola-anchor&quot; href=&quot;#small-cleanup&quot; aria-label=&quot;Anchor link for: small-cleanup&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The following is not strictly related to gulp4 but contains some of the things I like to do to have a clean gulpfile.
The config part at the top of the gulpfile (where I am defining all the paths to inject) is quite ugly but I am not going to touch it here, only change the gulp related things.&lt;&#x2F;p&gt;
&lt;p&gt;First thing, &lt;a href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;gulp-load-plugins&quot;&gt;gulp-load-plugins&lt;&#x2F;a&gt; makes things quite simple but the prefix I had chosen, &#x27;plugins&#x27;, is quite verbose and takes up space. Recently I&#x27;ve started using &lt;code&gt;$&lt;&#x2F;code&gt; which works quite well.
Something that took me way too long to realise is that it auto-camelCases packages names with dash so my following line was useless:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;js&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-js &quot;&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;plugins&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ngAnnotate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;gulp-ng-annotate&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Another thing to examine is whether some of the gulp plugins you&#x27;re using actually bring any values compared to using the actual npm module directly.
In my boilerplate, gulp-karma is not doing anything special and was only necessary while the karma team was fixing some of the issues the plugin was solving.&lt;&#x2F;p&gt;
&lt;p&gt;While going through the file, I also realised I didn&#x27;t test the watch process at all and that it didn&#x27;t work (surprising eh).&lt;&#x2F;p&gt;
&lt;p&gt;The fix was easy and I included it in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&#x2F;commit&#x2F;bee9fba6e9ee2602e96c8d735d409b31a267d655&quot;&gt;clean up commit&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, I have seen quite a few people dividing their tasks in individual files but that seems like an excessive measure to me.
Maybe it makes more sense if you have very long files but all of mine are between 50 and 200 lines (this one is actually my longest) so I never felt the need.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap up&lt;a class=&quot;zola-anchor&quot; href=&quot;#wrap-up&quot; aria-label=&quot;Anchor link for: wrap-up&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Some functions from gulp 3 such a &lt;code&gt;gulp.start&lt;&#x2F;code&gt; and &lt;code&gt;gulp.run&lt;&#x2F;code&gt; were deprecated and are not covered in this article since I never used them in any of my gulpfiles.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a bunch of new functions that I haven&#x27;t used yet. To have a complete overview of what&#x27;s new, you can check the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gulpjs&#x2F;gulp&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md#user-content-400&quot;&gt;gulp 4 changelog&lt;&#x2F;a&gt;.
Gulp 4 is still in alpha so I&#x27;ll update this article if any breaking change happens.&lt;&#x2F;p&gt;
&lt;p&gt;A question often asked is why use gulp when you have things like &lt;a href=&quot;http:&#x2F;&#x2F;webpack.github.io&#x2F;&quot;&gt;webpack&lt;&#x2F;a&gt; around but that&#x27;s out of scope for this article but will probably be part of another one.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Starting a company</title>
          <pubDate>Tue, 25 Nov 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/starting-a-company/</link>
          <guid>https://www.vincentprouillet.com/blog/starting-a-company/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/starting-a-company/">&lt;p&gt;I have been wanting to create my own company for a very long time, even before I graduated.&lt;&#x2F;p&gt;
&lt;p&gt;While it would have sucked an awful lot if I did it at that time for multiple reasons — lack of skills and experience mainly — it became more achievable recently.&lt;&#x2F;p&gt;
&lt;p&gt;In 2013, I tried my hand at a product company to solve the recruitment process called Hizard where we failed before even getting a single customer because of a lack of time and a lack of motivation: hiring sector was a safe bet, not something we were particularly interested in.&lt;&#x2F;p&gt;
&lt;p&gt;The code for Hizard is available on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hizardapp&#x2F;Hizard&quot;&gt;github&lt;&#x2F;a&gt;, a very simple django app.&lt;&#x2F;p&gt;
&lt;p&gt;I then worked as a contractor, which is nice for the 100% pay raise compared to a full-time employee.&lt;&#x2F;p&gt;
&lt;p&gt;The only issue with contracting is being an &#x27;ammo&#x27; can be loaded onto an existing project&#x2F;team to add features but more likely to fix bugs rather than having a project from the beginning, which I enjoy much more.&lt;&#x2F;p&gt;
&lt;p&gt;With two other friends (including the one I did Hizard with), we thought it would be better to start our own company to work on interesting projects and have more freedom to work on open-source things (one of the guy worked at Google, which is pretty strict regarding that).&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s how we decided to create an agency&#x2F;tech consultancy focusing on backends and &lt;em&gt;interesting&lt;&#x2F;em&gt; problems (think machine learning for example) rather than your run-of-the-mill digital agency doing CMS work.&lt;&#x2F;p&gt;
&lt;p&gt;The company was incorporated last week and its name is &lt;strong&gt;We Are Wizards&lt;&#x2F;strong&gt;, which was actually the name of my limited company as a contractor.&lt;&#x2F;p&gt;
&lt;p&gt;The website is at https:&#x2F;&#x2F;wearewizards.io (link now dead) and the twitter handle is @WeAreWizardsIO.&lt;&#x2F;p&gt;
&lt;p&gt;You can contact us through twitter or send an email to &lt;strong&gt;vincent@wearewizards.io&lt;&#x2F;strong&gt; if you want to talk about some projects you have.&lt;&#x2F;p&gt;
&lt;p&gt;You can also expect more articles coming on our blog once it&#x27;s live, we just need to write the first one !&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Turning a laptop into a CCTV</title>
          <pubDate>Mon, 22 Sep 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/turning-a-laptop-into-cctv/</link>
          <guid>https://www.vincentprouillet.com/blog/turning-a-laptop-into-cctv/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/turning-a-laptop-into-cctv/">&lt;p&gt;During the night of the 14th to the 15th of September, a glorious looking BBQ pulled pork sandwich I had put on my kitchen table was half eaten.
The bite was seriously huge, think a good 100g of bread + meat.
A previous accident, garbage bag being torn, suggested we might have a rodent issue in the house (or anything else really).
Being curious about who (could be a sleepeating housemate!)&#x2F;what the hell ate my lunch, we decided to investigate with my flatmates.
No droppings and the quantity of food eaten rule out mice — or at least we think so, unless we&#x27;re up against a colony of ninja mice — leaving us with rats, squirrels or stray cats: a small window was open in the kitchen, big enough for a small animal to enter.&lt;&#x2F;p&gt;
&lt;p&gt;The best way to find out what it is being actually seeing it, we decided to put some bait out and film the kitchen at night.
We don&#x27;t have a camera but we have webcams on our laptop.
How hard could it be to create a script to take pictures with it at a set interval ?
I ended up creating &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;rodent&quot;&gt;Rodent&lt;&#x2F;a&gt; to solve that issue.
One of my flatmates is a designer (Barbara Marcantonio, you can look at her &lt;a href=&quot;https:&#x2F;&#x2F;www.pixelspa.com&#x2F;&quot;&gt;website&lt;&#x2F;a&gt;) and made a logo for it for fun.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;Keats&#x2F;rodent&#x2F;master&#x2F;logo.png&quot; alt=&quot;Rodent&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Below is an explanation of how it works, this is pretty basic but that was new to me !&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation&quot; aria-label=&quot;Anchor link for: installation&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The installation is a bit tricky as it uses &lt;a href=&quot;http:&#x2F;&#x2F;opencv.org&#x2F;&quot;&gt;OpenCV&lt;&#x2F;a&gt; and needs a few dependencies for video&#x2F;images (you will need the libjpg one for the pictures at least).
The setup for Ubuntu is described in the README, I haven&#x27;t tried to install it on any other OS so I cannot comment on it.
Here&#x27;s a photo of actualy physical installation:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;setup_laptop.jpg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;setup_laptop.jpg&quot; alt=&quot;Laptop setup&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;setup_bait.jpg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;setup_bait.jpg&quot; alt=&quot;Bait setup&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We were not sure of what we are trying to catch so the bait is pretty much a bit of everything: nutella, bread, tuna, cheese etc&lt;&#x2F;p&gt;
&lt;h2 id=&quot;api&quot;&gt;API&lt;a class=&quot;zola-anchor&quot; href=&quot;#api&quot; aria-label=&quot;Anchor link for: api&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Rodent exposes 4 methods:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;capture: takes a picture from the webcam at a given &lt;code&gt;interval&lt;&#x2F;code&gt; forever or &lt;code&gt;until&lt;&#x2F;code&gt; the time specified in the &lt;code&gt;folder&lt;&#x2F;code&gt; given&lt;&#x2F;li&gt;
&lt;li&gt;make_video: takes all the pictures in the &lt;code&gt;folder&lt;&#x2F;code&gt; and makes a video out of it, better than watching pictures!&lt;&#x2F;li&gt;
&lt;li&gt;automate: does both capture and make_video, I use it for example to record until 15 minutes before I wake up and the video will be ready by the time I get to the kitchen&lt;&#x2F;li&gt;
&lt;li&gt;motion: takes a picture only if it detects a movement (the sexy part)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;let-s-get-down-to-business&quot;&gt;Let&#x27;s get down to business&lt;a class=&quot;zola-anchor&quot; href=&quot;#let-s-get-down-to-business&quot; aria-label=&quot;Anchor link for: let-s-get-down-to-business&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The first part I did was to take pictures at a set interval.
OpenCV exposes the webcam very simply:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Get the webcam
&lt;&#x2F;span&gt;&lt;span&gt;camera &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;VideoCapture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Take a picture, the ignored value being the return values
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;, image &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;camera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So this gives us an image, we now need to save it, once again OpenCV makes this very easy:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;filename &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;.jpg&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;(folder, now)
&lt;&#x2F;span&gt;&lt;span&gt;filepath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;filename
&lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;imwrite&lt;&#x2F;span&gt;&lt;span&gt;(filepath, image)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This works, but the main issue is that an image saved that way is around 64kB, which can be a lot if you take a picture every seconds for a whole day.
My first try was to open the image in PIL and save it right away while lowering the quality a bit:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Resave it with pillow to do a better compression
&lt;&#x2F;span&gt;&lt;span&gt;img &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Image.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span&gt;(filepath)
&lt;&#x2F;span&gt;&lt;span&gt;img.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;save&lt;&#x2F;span&gt;&lt;span&gt;(filepath, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;optimize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;quality&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;80&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I realised while writing that part that I could also specify the compression directly in OpenCV and skipping PIL entirely!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;filename &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;.jpg&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;filepath &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;filename &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;(folder, now)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Syntax is a bit odd but it works
&lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;imwrite&lt;&#x2F;span&gt;&lt;span&gt;(filepath, image, [cv2.cv.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;CV_IMWRITE_JPEG_QUALITY&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;80&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives an image of about 21kB, 3x times lower than the original one.
A pretty big save when multiplied by several thousands.&lt;&#x2F;p&gt;
&lt;p&gt;Putting it together with an interval, the code is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# check the start_camera function in rodent.py for the full method
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;, image &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;camera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    now &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;datetime.datetime.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    number &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Taking picture number &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt; at &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;(number, now.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;isoformat&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;    utils.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;save_image&lt;&#x2F;span&gt;&lt;span&gt;(image, folder, now)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;utils.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;time_over&lt;&#x2F;span&gt;&lt;span&gt;(until, now):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;break
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    time.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(interval)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A simple basic infinite loop that breaks on CTRL+C or if the time is over, nothing fancy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-ll-make-a-video-out-of-you&quot;&gt;I&#x27;ll make a video out of you&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-ll-make-a-video-out-of-you&quot; aria-label=&quot;Anchor link for: i-ll-make-a-video-out-of-you&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Once again, OpenCV provides pretty much everything needed to make a video out of the box.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# make_video in rodent.py
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Sorting on dates, ISO ftw
&lt;&#x2F;span&gt;&lt;span&gt;filenames &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;sorted&lt;&#x2F;span&gt;&lt;span&gt;(os.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;listdir&lt;&#x2F;span&gt;&lt;span&gt;(folder))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Find out size of the pictures we&amp;#39;re taking
&lt;&#x2F;span&gt;&lt;span&gt;first_pic &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;imread&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;(folder, filenames[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# shape gives a tuple (height, width, layer)
&lt;&#x2F;span&gt;&lt;span&gt;height, width, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;first_pic.shape
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# magic below, might need to change the codec for your own webcam
&lt;&#x2F;span&gt;&lt;span&gt;fourcc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.cv.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;CV_FOURCC&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;XVID&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;video &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;VideoWriter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;output.avi&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, fourcc, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, (width, height))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;filename &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;filenames:
&lt;&#x2F;span&gt;&lt;span&gt;    video.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;write&lt;&#x2F;span&gt;&lt;span&gt;(cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;imread&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;(folder, filename)))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;video.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;release&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The only tricky part is to take an image to get the size of the video and the fourcc parameter which is the 4-character code for the codec, which I pretty much tried randomly until I got one that works.
Right now the fps is determined completely arbitrarily depending on the number of pictures so it&#x27;s highly probable that it will be too slow or too fast, simply change the value of the 3rd parameter to VideoWriter.
The only issue is that it seems that OpenCV doesn&#x27;t write the last few images somehow.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-motion-detection&quot;&gt;The motion detection&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-motion-detection&quot; aria-label=&quot;Anchor link for: the-motion-detection&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s get to the interesting part, the motion detection.
The need for that arose after the first night of the home-made CCTV: watching minutes of footage of a still background is not very interesting.
Rather than taking a pic every second, how about only taking a pic only if movement is detected, that would reduce the number of pictures significantly and make looking at it less boring.
Since we only care about movements, all the pictures for that part are converted to grayscale:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;gray_pic &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;cvtColor&lt;&#x2F;span&gt;&lt;span&gt;(camera.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;read&lt;&#x2F;span&gt;&lt;span&gt;()[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;], cv2.cv.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;CV_RGB2GRAY&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;algorithms&quot;&gt;Algorithms&lt;a class=&quot;zola-anchor&quot; href=&quot;#algorithms&quot; aria-label=&quot;Anchor link for: algorithms&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The first obvious thing I tried was taking 2 pictures and doing a diff of them (again, OpenCV provides it through the &lt;a href=&quot;http:&#x2F;&#x2F;docs.opencv.org&#x2F;modules&#x2F;core&#x2F;doc&#x2F;operations_on_arrays.html#absdiff&quot;&gt;absdiff&lt;&#x2F;a&gt; method).
This worked okay-ish, the main issue being having ghosts in the motion.
Enter another algorithm, which I absolutely can&#x27;t remember where I read about it unfortunately so I don&#x27;t even know its name, which makes you compare 3 pictures instead of 2 and do a bitwise and between the 2 results.
This is what I currently use but a text explanation would be boring for a computer vision program so let&#x27;s walk through the code along with pictures.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How it works&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-works&quot; aria-label=&quot;Anchor link for: how-it-works&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We have 3 pictures: previous, current and the loop one.
We want to get the difference from the one in the loop compared to the previous and the current one.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;difference1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;absdiff&lt;&#x2F;span&gt;&lt;span&gt;(previous_image, gray_image)
&lt;&#x2F;span&gt;&lt;span&gt;difference2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;absdiff&lt;&#x2F;span&gt;&lt;span&gt;(current_image, gray_image)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In practice the difference looks like the following:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;difference.jpg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;difference.jpg&quot; alt=&quot;Difference with absdiff&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;You can see my outline, with a pretty cool effect imo, as I was moving in front of the camera.&lt;&#x2F;p&gt;
&lt;p&gt;Next we want to know the if something changed in both differences, done with the OpenCV bitwise_and method.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;bitwise_and&lt;&#x2F;span&gt;&lt;span&gt;(difference1, difference2)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives a result similar to the above, but usually more faint.
I personally love this kind of picture, it gives a watercolour like effect.
Here you can see my arm while I was standing up, a bit on profile:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;bitwise_and.jpg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;bitwise_and.jpg&quot; alt=&quot;Result after bitwise_and&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;To make it clearer and know what you&#x27;re looking at, I tried to highlight the outline of my body:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;bitwise_and_contour.jpg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;bitwise_and_contour.jpg&quot; alt=&quot;Result after bitwise_and outline&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;We then want to apply a binary threshold to only get clear motions:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;, result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;threshold&lt;&#x2F;span&gt;&lt;span&gt;(result, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;255&lt;&#x2F;span&gt;&lt;span&gt;, cv2.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;THRESH_BINARY&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This means that for every pixel in the result, turn those with a value above 40 (arbitrary value, seems to give good results but open to changing it!) to 255 (white).
This is the picture above after the thresholding:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;threshold.jpg&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;threshold.jpg&quot; alt=&quot;Result after thresholding&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;A picture after threshold with no motion would be completely black.&lt;&#x2F;p&gt;
&lt;p&gt;We now have an image with the areas where a motion occured in white and we only need to check if we have some pixels with a value of 255 in the image to know if there was a motion.
Since we know that, we might as well highlight the area where a movement occured: Rodent does that by putting a purple rectangle around the area.
The code is not that interesting and lives in utils.py&#x2F;find_motion_boundaries if you want to check it out.
It simply finds the lowest&#x2F;highest point (X, Y) in the image with 255, becoming the coordinates for the rectangle and None if there was no motion.
Adding a rectangle to an image is trivial as well:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;# low_point and high_point are the (X,Y) tuple mentioned above
&lt;&#x2F;span&gt;&lt;span&gt;# purple is the colour in RGB and 3 is the thickness of the line in pixels.
&lt;&#x2F;span&gt;&lt;span&gt;cv2.rectangle(image, low_point, high_point, purple, 3)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that the rectangle method does the transformation in-place, it doesn&#x27;t return the new image.
Here&#x27;s what a motion made into a video looks like:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;motion.gif&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;turning-a-laptop-into-cctv&#x2F;motion.gif&quot; alt=&quot;Motion detection&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;a class=&quot;zola-anchor&quot; href=&quot;#results&quot; aria-label=&quot;Anchor link for: results&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We still haven&#x27;t caught the culprit but Rodent has been quite fun so far !
This is the first time I&#x27;m using OpenCV and it is very powerful, the API is not very pythonic but works very well.
Also, don&#x27;t forget to delete the camera object because it happened to me that the webcam refused to turn on until I rebooted after a several CTRL+C, it is now handled properly by catching KeyboardInterrupt.&lt;&#x2F;p&gt;
&lt;p&gt;The space below is reserved for a picture of the culprit once we catch him.&lt;&#x2F;p&gt;
&lt;p&gt;[Insert screenshot of culprit once caught]&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Finding trending things in Elasticsearch using python and redis</title>
          <pubDate>Fri, 19 Sep 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/finding-trending-things/</link>
          <guid>https://www.vincentprouillet.com/blog/finding-trending-things/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/finding-trending-things/">&lt;p&gt;One of the features at my current contract is displaying widgets showing trending news articles.
We define trendiness on a company basis (topic as well but let&#x27;s keep it to only companies for this article), ie we want to display articles from companies that are trending.
The articles are stored in &lt;a href=&quot;http:&#x2F;&#x2F;www.elasticsearch.org&#x2F;&quot;&gt;Elasticsearch&lt;&#x2F;a&gt; and we want to spot the trending ones for a given time period (last week, last month, last quarter, last year).
For each article we keep track (in the article doc in ES, as a &lt;a href=&quot;http:&#x2F;&#x2F;www.elasticsearch.org&#x2F;guide&#x2F;en&#x2F;elasticsearch&#x2F;reference&#x2F;current&#x2F;mapping-nested-type.html&quot;&gt;nested type&lt;&#x2F;a&gt;) of the companies mentioned and we use that field to calculate the trends (this is a bit simplified, there is actually a second similar field as well but let&#x27;s keep things simple for the sake of the article).&lt;&#x2F;p&gt;
&lt;p&gt;These trending articles are shown in a iframe and this iframe had been timing out even for short-ish time periods (3 months or so) worth of data.
Since we can&#x27;t really have things timing out, I decided to have a look and try to improve things.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-was-there-before&quot;&gt;What was there before&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-was-there-before&quot; aria-label=&quot;Anchor link for: what-was-there-before&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Looking at the code, I realised that the current trending code was very naive.
It was working in two steps:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;gets the number of times a company was mentioned over the time period we&#x27;re interested in&lt;&#x2F;li&gt;
&lt;li&gt;run a javascript script in a ES query that was calculating the score for each article by adding the score for of each company in the article&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are two things wrong with that approach.
The first obvious one is that it massively favours big companies, as they will have more articles talking about them, and even if they are actually trending down compared to the norm, they will still be at the top of the trending list.
The second one is that it is running a script iterating over 2 dicts for each article (2 because as mentioned in the introduction, there is another field we rate with in addition to companies), making it pretty damn slow and timing out on the live server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-new-approach&quot;&gt;A new approach&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-new-approach&quot; aria-label=&quot;Anchor link for: a-new-approach&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With these 2 things in mind, I set out to figure out a better and faster way to find the trending companies.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;trendiness&quot;&gt;Trendiness&lt;a class=&quot;zola-anchor&quot; href=&quot;#trendiness&quot; aria-label=&quot;Anchor link for: trendiness&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The first thing to define is trendiness: something trendy is something that is mentioned more often than usual.
From that definition we can realise that we first need to define what is &lt;em&gt;usual&lt;&#x2F;em&gt;, also called the &lt;em&gt;baseline&lt;&#x2F;em&gt; so let&#x27;s start with that.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;defining-a-baseline-using-elasticsearch&quot;&gt;Defining a baseline using Elasticsearch&lt;a class=&quot;zola-anchor&quot; href=&quot;#defining-a-baseline-using-elasticsearch&quot; aria-label=&quot;Anchor link for: defining-a-baseline-using-elasticsearch&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As mentioned above, the goal in defining a baseline is finding out what&#x27;s normal for a company.
I chose to find the number of mentions for each company everyday for the 3 months prior to the interval we&#x27;re interested in.
3 months is a completely arbitrary value that could as well be 1 month but it seemed about right.
Elasticsearch provides a &lt;a href=&quot;http:&#x2F;&#x2F;www.elasticsearch.org&#x2F;guide&#x2F;en&#x2F;elasticsearch&#x2F;reference&#x2F;current&#x2F;search-aggregations-bucket-datehistogram-aggregation.html&quot;&gt;date histogram aggregation&lt;&#x2F;a&gt; that does exactly that !&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s have a look at a query, there are a few options worth mentioning:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;size&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;query&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;},
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;aggs&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;companies&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;date_histogram&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;field&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;harvest_date&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;interval&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;day&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;format&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;yyyy-MM-dd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;min_doc_count&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;extended_bounds&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;min&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;history_start,&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;max&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                },
&lt;&#x2F;span&gt;&lt;span&gt;            },
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;aggs&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;nested_items&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;aggs&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;items&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;terms&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;field&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;companies.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;size&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span&gt;                            }
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                    },
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;nested&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;path&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;companies&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As mentioned in the introduction, companies is a nested type so require some addition levels in the queries.
There are a few things to note:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;we want a day by day interval, ES provides that out of the box and a few more if necessary, see the link above for all the possibilities&lt;&#x2F;li&gt;
&lt;li&gt;we want the date formatted as ISO for simplicity sake&lt;&#x2F;li&gt;
&lt;li&gt;we want to get as many buckets as possible so we even get the days where we don&#x27;t have any matching documents by setting &lt;strong&gt;min_doc_count&lt;&#x2F;strong&gt; to 0&lt;&#x2F;li&gt;
&lt;li&gt;we want every possible day between history_start and end to have a bucket by setting the &lt;strong&gt;extended_bounds&lt;&#x2F;strong&gt; min&#x2F;max to these dates&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With this query we get a bucket for each day that can contain companies with they doc_count if there are, or an empty bucket otherwise.
With all the companies ids and that data, we can recreate the complete histogram of the number of article for each company in our postgres  database.  I also separate the history data from the window we are observing, the python code looks something like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;AggregationData &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;namedtuple&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;AggregationData&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;history&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;window&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;_get_numbers_by_day&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;all_ids&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;aggregation&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;window_start&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;window_start is ISO formatted string&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  values &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;defaultdict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;AggregationData&lt;&#x2F;span&gt;&lt;span&gt;([], []))
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  in_window &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;False
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;day &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;aggregation:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if not &lt;&#x2F;span&gt;&lt;span&gt;in_window:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# remember we asked for the dates from the agg to be ISO formatted as well
&lt;&#x2F;span&gt;&lt;span&gt;      in_window &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;window_start &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span&gt;day[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;key_as_string&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# then look if we got doc_counts for that day, add it to the right tuple in the values dict, see below
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# fill with 0 for the rest, for example
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# (_id would have been set while looping over the ids not seen in the buckets here)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;in_window:
&lt;&#x2F;span&gt;&lt;span&gt;      values[_id].window.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      values[_id].history.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We now have the data for all companies for each day. Cool.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;finding-the-trends&quot;&gt;Finding the trends&lt;a class=&quot;zola-anchor&quot; href=&quot;#finding-the-trends&quot; aria-label=&quot;Anchor link for: finding-the-trends&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;first-approach&quot;&gt;First approach&lt;a class=&quot;zola-anchor&quot; href=&quot;#first-approach&quot; aria-label=&quot;Anchor link for: first-approach&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The first thing I tried was to get the mean value of the history values and divide the window period values with that mean to get normalized values compared to their usual values.
You are then able to spot unusual activities when a value is above 1 (by that mean I something like 5, not 1.1) and identify trends by looking at the difference between 2 consecutive points: if the numbers are going up and are reasonably higher than 1, it&#x27;s trending !&lt;&#x2F;p&gt;
&lt;p&gt;While this gives &lt;em&gt;ok&lt;&#x2F;em&gt; results, this approach fails to account for the standard deviation which can change the results quite a bit.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;z-score&quot;&gt;Z-Score&lt;a class=&quot;zola-anchor&quot; href=&quot;#z-score&quot; aria-label=&quot;Anchor link for: z-score&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Time to look at &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Standard_score&quot;&gt;z-score&lt;&#x2F;a&gt; !
This is the standard algorithm to find trending things and is simple to implement:&lt;&#x2F;p&gt;
&lt;div class=&quot;post__image&quot;&gt;
    &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;finding-trending-things&#x2F;http:&amp;#x2F;&amp;#x2F;upload.wikimedia.org&amp;#x2F;math&amp;#x2F;8&amp;#x2F;4&amp;#x2F;6&amp;#x2F;8463971a22cc96a1e0612588e5656bce.png&quot; class=&quot;image-link&quot; target=&quot;_blank&quot;&gt;
        &lt;img src=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;finding-trending-things&#x2F;http:&amp;#x2F;&amp;#x2F;upload.wikimedia.org&amp;#x2F;math&amp;#x2F;8&amp;#x2F;4&amp;#x2F;6&amp;#x2F;8463971a22cc96a1e0612588e5656bce.png&quot; alt=&quot;z-score formula&quot;&gt;
    &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;With μ being the history mean and σ the standard deviation of the history data.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s implement it quickly in python:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;math &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;sqrt
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;zscore&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;point&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Going to observe a single point here&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  length_data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(data)) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# need floats, and we are using it several times
&lt;&#x2F;span&gt;&lt;span&gt;  mean &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(data) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;length_data  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# and be careful of len(data) == 0
&lt;&#x2F;span&gt;&lt;span&gt;  std &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sqrt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;((point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;mean) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;** &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;data) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;length_data)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# And we now apply the formula above
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;mean) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;std  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# again, check for std == 0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nothing fancy going there, just getting the mean and standard deviation for the data and use the formula.
This gives pretty good results (good thing we humans can detect if something is trendy pretty easily):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt; zscore(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;20, 10, 10, 5, 5, 6, 6, 6&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;, 20)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;.4244128728
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt; zscore(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;0, 2, 3, 4, 6, 8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;, 2)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;0.7027642215
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There is one issue with that though:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;zscore&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.15470053838
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;zscore&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.15470053838
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our guts tell us that the first one is more unusual than the second one and thus should have a higher rating than the second (imagine similar series containing hundred of points rather than this small example) if we are only looking at the last few days.
When we are looking at trending things, recent values are more important than old ones right?
We need to somehow depreciate the previous values as we move forward in the history.
This is called a rolling zscore and unfortunately Pandas doesn&#x27;t &lt;a href=&quot;https:&#x2F;&#x2F;pandas.pydata.org&#x2F;pandas-docs&#x2F;stable&#x2F;reference&#x2F;window.html#standard-moving-window-functions&quot;&gt;have one for zscore&lt;&#x2F;a&gt;.  I could have probably used rolling_apply but where would be the fun in that and it is pretty easy to implement anyway !&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rolling-z-score&quot;&gt;Rolling z-score&lt;a class=&quot;zola-anchor&quot; href=&quot;#rolling-z-score&quot; aria-label=&quot;Anchor link for: rolling-z-score&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;We got the formula and the code for the normal z-score above.
By rolling we mean that we re-apply the formula for every point, and in our case adding a factor so that the oldest points carry the less value.
How do we do that?
Simply by multiplying the average by a factor for every point.
An implementation in python looks like the following (maths taken from &lt;a href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;826509&quot;&gt;that stackoverflow answer&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;observed_window&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;decay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.9&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    The lowest the decay, the more important the new points
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    Decay is there to ensure that new data is worth more than old data
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    in terms of trendiness
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Set the average to a the first value of the history to start with
&lt;&#x2F;span&gt;&lt;span&gt;    avg &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;(data[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;    squared_average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;(data[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;** &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;add_to_history&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;point&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;average&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sq_average&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;decay &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;decay)
&lt;&#x2F;span&gt;&lt;span&gt;        sq_average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;sq_average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;decay &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;(point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;** &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;decay)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;average, sq_average
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;calculate_zscore&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;average&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sq_average&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        std &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sqrt&lt;&#x2F;span&gt;&lt;span&gt;(sq_average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;avg &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;** &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;std &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;average
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;(value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span&gt;average) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span&gt;std
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;data[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:]:
&lt;&#x2F;span&gt;&lt;span&gt;        avg, squared_average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;add_to_history&lt;&#x2F;span&gt;&lt;span&gt;(point, avg, squared_average)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    trends &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[]
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# We recalculate the averages for each new point added to be more
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# accurate
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;point &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;observed_window:
&lt;&#x2F;span&gt;&lt;span&gt;        trends.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;calculate_zscore&lt;&#x2F;span&gt;&lt;span&gt;(avg, squared_average, point))
&lt;&#x2F;span&gt;&lt;span&gt;        avg, squared_average &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;add_to_history&lt;&#x2F;span&gt;&lt;span&gt;(point, avg, squared_average)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Close enough way to find a trend in the window
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(trends) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(trends) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(trends) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s see the results and how decay affects the trendiness by checking the values for the trends list:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Values used, you can see data averaging 3-4
&lt;&#x2F;span&gt;&lt;span&gt;data &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;9&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;window_not_trending &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;window_trending &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;(data, window_not_trending)
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3.8618524915490227e-05&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.5000347566724239&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.04996871899481836&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.5449718470953364&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.8904746623858029&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.6985728038527774&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.1287155234674997&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.0225790751369
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;(data, window_trending)
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.0000386185249155&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.400034756672424&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.106687520670121&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.5626854352697754&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.4798126688070985&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.185465121541111&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.566918609387&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.18594896155
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# And now with diferent decay, not linear relation
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;(data, window_trending, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;decay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.85740988579
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;(data, window_trending, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;decay&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.93406854599
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And now let&#x27;s see the results for the type of series we had before that would cause problems:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.03674495279
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;], [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.062882
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ok we got something that looks like credible results, time to get back to the ES part but first, a (very) quick trip to redis.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;finding-the-most-trendings-in-that&quot;&gt;Finding the most trendings in that&lt;a class=&quot;zola-anchor&quot; href=&quot;#finding-the-most-trendings-in-that&quot; aria-label=&quot;Anchor link for: finding-the-most-trendings-in-that&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;So we got a dict with companies ids as keys and trendiness as values.
We could easily sort that in python but where&#x27;s the fun in that !
Redis provides a sorted set for us that we can query easily as we want and since we want to cache things to avoid repeating all these calculations and the initial ES query, let&#x27;s use that: &lt;a href=&quot;http:&#x2F;&#x2F;redis.io&#x2F;commands&#x2F;zadd&quot;&gt;ZADD&lt;&#x2F;a&gt;.
The syntax is a bit odd (to me) as you five the value before the key, but is easy to use:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# This will insert a member to the set `company_key`,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# with the rolling average as the value and the company_id as they key
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;company_id, agg &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;companies.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;iteritems&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    redis.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;zadd&lt;&#x2F;span&gt;&lt;span&gt;(company_key, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;rolling_zscore&lt;&#x2F;span&gt;&lt;span&gt;(agg.history, agg.window), company_id)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, if we query redis for that set, it will return the key-value tuple sorted the way we want:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# This asks for the 5 companies with the highest trending score
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# and to send the score back as well
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# if you want to find the 5 lowest trending companies, you would use redis.zrange
&lt;&#x2F;span&gt;&lt;span&gt;trending_companies &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;redis.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;zrevrange&lt;&#x2F;span&gt;&lt;span&gt;(company_key, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;withscores&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;wrapping-this-up-in-elasticsearch&quot;&gt;Wrapping this up in Elasticsearch&lt;a class=&quot;zola-anchor&quot; href=&quot;#wrapping-this-up-in-elasticsearch&quot; aria-label=&quot;Anchor link for: wrapping-this-up-in-elasticsearch&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Note: I am a newbie with ES, so do let me know if there are better ways to do that
We got our trending companies, time to actually take them into articles when fetching data from ES.
ES provides a way to &lt;a href=&quot;http:&#x2F;&#x2F;www.elasticsearch.org&#x2F;guide&#x2F;en&#x2F;elasticsearch&#x2F;reference&#x2F;1.x&#x2F;query-dsl-boosting-query.html&quot;&gt;boost&lt;&#x2F;a&gt; a query, which will change a document score (ie 0.2 boost means its the document score is multiplied by 0.2 and so has a lower score while a boost of 1.5 means it will be higher than normal).
Good thing we have the trendiness of every companies ! We can just give each company its trendiness as a boost:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;companies_filters &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# trending_companies if the list of tuples returned by redis.zrevrange
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;company &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;trending_companies:
&lt;&#x2F;span&gt;&lt;span&gt;    companies_filters.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;filter&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;nested&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;path&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;companies&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;filter&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;term&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;companies.id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: company[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# id
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;boost_factor&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;company[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# score (can be negative)
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;From that we get a list of filters to apply to our query that will favour articles from the trendy companies.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I finally got to play a bit with Elasticsearch and it looks quite good !
Being able to do queries in JSON (still more complex than SQL imo) and easy to compose it from different functions as from the python side we are just manipulating a dict.
I&#x27;ll definitely use it when I need search on another project.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Speeding up Django &#x2F; PostgreSQL</title>
          <pubDate>Wed, 20 Aug 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/speeding-up-django-postgres/</link>
          <guid>https://www.vincentprouillet.com/blog/speeding-up-django-postgres/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/speeding-up-django-postgres/">&lt;h2 id=&quot;background&quot;&gt;Background&lt;a class=&quot;zola-anchor&quot; href=&quot;#background&quot; aria-label=&quot;Anchor link for: background&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At my current contract, we process hundreds of thousand of articles a day and those are saved in some different models as we go along the pipeline. The stack for that application is Django (1.6) + PostgreSQL (9.3) + &lt;a href=&quot;http:&#x2F;&#x2F;python-rq.org&#x2F;&quot;&gt;rq&lt;&#x2F;a&gt; (alternative to the classic Celery for the queues).
Tasks started to take way too much time and so I started looking into.
This is not going to mention caching but things should obviously be cached if it is possible in your project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;speeding-up-django-queries&quot;&gt;Speeding up Django queries&lt;a class=&quot;zola-anchor&quot; href=&quot;#speeding-up-django-queries&quot; aria-label=&quot;Anchor link for: speeding-up-django-queries&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The PostgreSQL database being on his own server, I assumed it was properly configured (spoiler: it wasn&#x27;t) and thus started the investigation in the django project.
The models in that case are news articles, so it contains the text of the article (and it can get pretty big) as well as lots of metadata about it.
All in all, one of these objects can get pretty big (relatively speaking, keep in mind there are tens of millions of them) so getting&#x2F;saving them can take some time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;only&quot;&gt;Only&lt;a class=&quot;zola-anchor&quot; href=&quot;#only&quot; aria-label=&quot;Anchor link for: only&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Most of the time you do not need to get the whole object, you might want to only get one or two fields, very simple thing to do in SQL and in Django as well thanks to .only():&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Equivalent to SELECT id FROM myapp_article;
&lt;&#x2F;span&gt;&lt;span&gt;Article.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;only&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;id&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Note that for .get(), only needs to be placed before .get() as only() is a method
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# of Queryset and .get() returns a model
&lt;&#x2F;span&gt;&lt;span&gt;Article.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;only&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;id&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That sped up things in some places, but it was still pretty damn slow.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;update-fields&quot;&gt;Update fields&lt;a class=&quot;zola-anchor&quot; href=&quot;#update-fields&quot; aria-label=&quot;Anchor link for: update-fields&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Looking at the slow queries log (more on that in the postgres part below), the issue was very clear: when updating an article on one field, it was re-saving everything !
Django&#x27;s ORM comes with an easy way to fix that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Equivalent to UPDATE myapp_article SET url=&amp;#39;http:&#x2F;&#x2F;www.google.com&amp;#39; WHERE id = 42;
&lt;&#x2F;span&gt;&lt;span&gt;article &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Article.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;article.url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;http:&#x2F;&#x2F;www.google.com&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# article.save() -&amp;gt; naive way, will update every field
&lt;&#x2F;span&gt;&lt;span&gt;article.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;save&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;update_fields&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;url&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# correct way
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;bulk-create&quot;&gt;Bulk create&lt;a class=&quot;zola-anchor&quot; href=&quot;#bulk-create&quot; aria-label=&quot;Anchor link for: bulk-create&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another thing to try was to bulk insert articles instead of creating them while looping:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;to_insert &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;article &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;articles:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;needs_saving:
&lt;&#x2F;span&gt;&lt;span&gt;    to_insert.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(article)  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# article is a dict here
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Article.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;bulk_create&lt;&#x2F;span&gt;&lt;span&gt;([
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Article&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt;article) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;article &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;to_insert
&lt;&#x2F;span&gt;&lt;span&gt;])
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are a few caveats to be aware of when using using bulk_create, those are explained in the &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;dev&#x2F;ref&#x2F;models&#x2F;querysets&#x2F;#bulk-create&quot;&gt;django doc&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s three problems fixed. Still slow though.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mass-update&quot;&gt;Mass update&lt;a class=&quot;zola-anchor&quot; href=&quot;#mass-update&quot; aria-label=&quot;Anchor link for: mass-update&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In the same spirit of the bulk_create, mass update will also speed up your code quite a bit:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;ids_to_update &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Code that appends to ids_to_update
&lt;&#x2F;span&gt;&lt;span&gt;Article.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;id__in&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;ids_to_update).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;failed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will be much faster than doing one update for each article individually as it will be done in a single SQL &#x27;UPDATE&#x27; query.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;indices&quot;&gt;Indices&lt;a class=&quot;zola-anchor&quot; href=&quot;#indices&quot; aria-label=&quot;Anchor link for: indices&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another one was to add an index on a column that was used to filter but wasn&#x27;t an index at all:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;external_id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;models.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;IntegerField&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;db_index&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# And then run a south or django 1.7+ migration
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Adding an index in postgres is pretty damn fast so do not be worried about the time it could take and sped up those queries significantly (ie they do not appear in the slow queries log).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;transactions&quot;&gt;Transactions&lt;a class=&quot;zola-anchor&quot; href=&quot;#transactions&quot; aria-label=&quot;Anchor link for: transactions&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The last trick is to wrap a method&#x2F;bit of code to ensure atomicity.
Django provides a method usable both as a decorator and as a context manager allowing to do that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# As a decorator
&lt;&#x2F;span&gt;&lt;span&gt;@transaction.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;atomic
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;do_lots_of_queries&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;pass
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# As a context manager
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span&gt;transaction.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;atomic&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# do queries
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;pass
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This prevents from commiting every query and can speed up the piece of code significantly.
Use that sparingly though as transactions can have some performance cost, make sure it&#x27;s actually better before putting everything in transactions.&lt;&#x2F;p&gt;
&lt;p&gt;After all these changes, the processing was faster but still super slow.
I decided to then look at the DB server but just an additional tip before.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bonus-select-related-and-prefetch-related&quot;&gt;Bonus: select_related and prefetch_related&lt;a class=&quot;zola-anchor&quot; href=&quot;#bonus-select-related-and-prefetch-related&quot; aria-label=&quot;Anchor link for: bonus-select-related-and-prefetch-related&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another thing I didn&#x27;t use there (because in my case I didn&#x27;t need to select related objects) is &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;dev&#x2F;ref&#x2F;models&#x2F;querysets&#x2F;#django.db.models.query.QuerySet.select_related&quot;&gt;select_related&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;dev&#x2F;ref&#x2F;models&#x2F;querysets&#x2F;#prefetch-related&quot;&gt;prefetch_related&lt;&#x2F;a&gt;.
Those are used when you need to get associated models you know you will need later in your code and don&#x27;t want to do a query for each.
select_related is used where you have a one-to-many or one-to-one (and do a JOIN in SQL in one query) and prefetch_related for many-to-one and many-to-many (do one query per type of object and join them in python).
This can shave a huge amount of queries from your total number of queries: at my last job it went from ~1000 to ~50 just by using those.
For example, let&#x27;s say you have an Article and Author model and you want to display a template with the article content and the author data.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Let&amp;#39;s assume there are 50 articles
&lt;&#x2F;span&gt;&lt;span&gt;articles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Article.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;select_related&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;author&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% for &lt;&#x2F;span&gt;&lt;span&gt;article &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span&gt;articles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;  {{ article }} {{ article.author.name }}  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# without select_related this will do an additional query per loop
&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span&gt;endfor &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# With select_related:  1 query
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Without: 51 queries
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can easily see from the example how it helps A LOT.
To easily spot the places where you can&#x2F;should use them, install &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;django-debug-toolbar&#x2F;django-debug-toolbar&quot;&gt;django-debug-toolbar&lt;&#x2F;a&gt; and look at the SQL panels.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;postgres-speed-improvement&quot;&gt;Postgres speed improvement&lt;a class=&quot;zola-anchor&quot; href=&quot;#postgres-speed-improvement&quot; aria-label=&quot;Anchor link for: postgres-speed-improvement&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing I did (and I actually did it before doing most changes I mentioned in the django part) is to activate the slow queries logging.
This will make postgres log everything that takes can more than X seconds (where you define X).
All the changes mentioned below are done in postgresql.conf, located in &#x2F;etc&#x2F;postgresql&#x2F;9.3&#x2F;main (change 9.3 for your version obviously).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-ini &quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# allows to log in the log directory specified below
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;logging_collector &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;on
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# with that setting, logs will be saved in &#x2F;var&#x2F;lib&#x2F;postgresql&#x2F;9.3&#x2F;main&#x2F;pg_log&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;log_directory &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;pg_log&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Log queries that take more than 1s (I told you it was slow)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;log_min_duration_statement &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1000
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This allowed me to detect the slow queries caused by the absence of .only() and update_fields.&lt;&#x2F;p&gt;
&lt;p&gt;After doing all the fixes in the Django code I was still seing ridiculously slow queries (a good number were taking more than 20s).
Now when I added the slow queries logging, I noticed the absence of custom configuration at the bottom of the file.
Surely if there&#x27;s a server dedicated to the DB, postgres will have been tuned to use all the amazing amount of RAM available (I initially thought I had a bug when free -m told me the server had 64go of memory).
The tuning looks something like that (see the &lt;a href=&quot;https:&#x2F;&#x2F;wiki.postgresql.org&#x2F;wiki&#x2F;Tuning_Your_PostgreSQL_Server&quot;&gt;guide&lt;&#x2F;a&gt; to know which values to choose):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-ini &quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;log_min_duration_statement &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1000
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;max_connections &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;100
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;shared_buffers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;15GB
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;effective_cache_size &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;45GB
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work_mem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;78643kB
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;maintenance_work_mem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2GB
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;checkpoint_segments &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;128
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;checkpoint_completion_target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;9
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wal_buffers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;16MB
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;default_statistics_target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;500
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This made everything run about 10x faster.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Most of the time, the biggest bottleneck will be the database so do not forget that you are not using SQL and therefore you might make mistakes using an ORM (forgetting the update_fields for example) that you would never do in SQL.
The first thing to check would be the postgres config if queries are running slow but don&#x27;t forget to optimize the django part as well.
In this project, some queries will still require tuning through EXPLAIN (probably worth a different article).
Including the postgres tuning and the django changes, the code is running 10 and 100x faster than it was before.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Trying out Rust</title>
          <pubDate>Sat, 19 Jul 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/trying-out-rust/</link>
          <guid>https://www.vincentprouillet.com/blog/trying-out-rust/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/trying-out-rust/">&lt;p&gt;Rust is an in-progress systems programming language (&lt;a href=&quot;http:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;official website&lt;&#x2F;a&gt;).
I&#x27;ll start this post by saying I&#x27;m a python&#x2F;js&#x2F;go developer and never really done C or C++ (my first programming languages were PHP&#x2F;C# actually since I started with a degree in biology).
Rust is mainly targeting those C&#x2F;C++ developers that need speed (without a GC) and safety.
As a guy who does only web stuff, the absence&#x2F;presence of a GC is not important since you can use slow languages like python or ruby and be completely fine with it for 99% of the sites so a GC pause probably won&#x27;t even be noticed unlike in video games for example.
Why bother learning low level languages then?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s fun&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s fast&lt;&#x2F;li&gt;
&lt;li&gt;It has types (I love types)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is just my feelings after playing with it a few hours, it may contain mistakes and some stuff will probably be outdated pretty soon anyway.
Like every article I&#x27;ve read, I&#x27;ll be comparing some of the features with Go but do note that I like them both.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-rust&quot;&gt;What&#x27;s Rust?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-rust&quot; aria-label=&quot;Anchor link for: what-s-rust&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned above, Rust is a language developed by Mozilla in the open: all the code is on github, decisions are taken by committee.
A stable release is expected around Q4 of 2014.
You can follow or even submit&#x2F;comment on RFCs on the language: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rfcs&quot;&gt;RFCs repo&lt;&#x2F;a&gt;, decisions on these RFCs being taken in meetings for which you can find the transcripts on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;meeting-minutes&quot;&gt;github as well&lt;&#x2F;a&gt;.
The language itself is also in github in case you didn&#x27;t guess: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&quot;&gt;rust repo&lt;&#x2F;a&gt;.
More discussions also happen on reddit (&lt;a href=&quot;http:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&quot;&gt;&#x2F;r&#x2F;rust&lt;&#x2F;a&gt;) and on the &lt;a href=&quot;https:&#x2F;&#x2F;users.rust-lang.org&#x2F;&quot;&gt;discourse forum&lt;&#x2F;a&gt;.
I talked quite a bit about the community because I find looking at the evolution quite fascinating, let&#x27;s talk a bit about the language itself now.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s use bullet points and then go through those individually&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Statically and strongly typed with generics (yay!)&lt;&#x2F;li&gt;
&lt;li&gt;Memory safety&lt;&#x2F;li&gt;
&lt;li&gt;Easy concurrency with no data race&lt;&#x2F;li&gt;
&lt;li&gt;Functional stuff&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;type-system&quot;&gt;Type system&lt;a class=&quot;zola-anchor&quot; href=&quot;#type-system&quot; aria-label=&quot;Anchor link for: type-system&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;It is pretty cool.
Variables are immutable by default and the compiler will complain if you are using a variable before initalizing it (not very idiomatic in Rust to initialize a variable after declaring it apparently anyway) or if it&#x27;s unused (you can make that fail compilation if you want).
It has generics which allows some cool stuff without passing interface{} if you wanted to do something similar in Go (let&#x27;s say an ORM).
It uses traits rather than the classic interfaces OOP languages, if you&#x27;ve used Scala it will be familiar.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;memory-safety&quot;&gt;Memory safety&lt;a class=&quot;zola-anchor&quot; href=&quot;#memory-safety&quot; aria-label=&quot;Anchor link for: memory-safety&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Part of that section could go in the type system section but since this is the complex part of rust for newcomer like me I feel it deserves its own section.
There are 3 types of pointers in Rust:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;owned pointers: the classic ones, compiler takes care of them automatically&lt;&#x2F;li&gt;
&lt;li&gt;borrowed pointers: as the name implies, you can use them when you want to access data, but without taking ownership of it&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ownership (and mainly the lifetimes that go with it) is a major point of Rust and documentation will do a better job than me at explaining them: &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;1.36.0&#x2F;book&#x2F;ch10-03-lifetime-syntax.html&quot;&gt;The Rust References and Lifetimes Guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;easy-concurrency-with-no-data-race&quot;&gt;Easy concurrency with no data race&lt;a class=&quot;zola-anchor&quot; href=&quot;#easy-concurrency-with-no-data-race&quot; aria-label=&quot;Anchor link for: easy-concurrency-with-no-data-race&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Concurrency in Rust is as easy as in Go, here&#x27;s a very basic example:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; run on http:&#x2F;&#x2F;is.gd&#x2F;kDPsLe
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(tx, rx): (Sender&amp;lt;int&amp;gt;, Receiver&amp;lt;int&amp;gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;channel&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;proc&lt;&#x2F;span&gt;&lt;span&gt;() { tx.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;); });
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; rx.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;recv&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It is very easy to read and I actually find it easier to have the named channels like this than reading from the channel.
The &quot;no data race&quot; part comes from the fact that you cannot access mutable data inside a task (It won&#x27;t even let you reuse the tx variable here in another as you can see in that &lt;a href=&quot;http:&#x2F;&#x2F;is.gd&#x2F;ncfyM4&quot;&gt;playpen&lt;&#x2F;a&gt;.
Trying to modify a mutable structure for example would result in a compiler error: &lt;a href=&quot;http:&#x2F;&#x2F;is.gd&#x2F;5AFfTn&quot;&gt;playpen&lt;&#x2F;a&gt;.
An in-depth explanation is also present in the docs: &lt;a href=&quot;http:&#x2F;&#x2F;doc.rust-lang.org&#x2F;guide-tasks.html&quot;&gt;The Rust Tasks and Communication Guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;functional-stuff&quot;&gt;Functional stuff&lt;a class=&quot;zola-anchor&quot; href=&quot;#functional-stuff&quot; aria-label=&quot;Anchor link for: functional-stuff&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;m usually a bit wary of functional elements as it can sometimes be very hard to read and understand at a glance what a functional programmer did when he was being &#x27;neat&#x27;.
Nevertheless, functional programming also brings very very cool stuff:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;options&quot;&gt;Options&lt;a class=&quot;zola-anchor&quot; href=&quot;#options&quot; aria-label=&quot;Anchor link for: options&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;An idiomatic Go function will usually return 2 elements: a value and an error. You will have to check whether the error is nil or not and handle both cases&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;books&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;GetBooks&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;nil&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;books &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;books&lt;&#x2F;span&gt;&lt;span&gt;[:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; will give a nil error if we didn&amp;#39;t check the error above
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;books&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;nil
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I don&#x27;t mind explicit error handling but it&#x27;s easy to forget it somewhere and end up with a null pointer exception.
Options are basically nullable types that forces you to handle them, enforced by the compiler.
It is typically handled using pattern matching.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pattern-matching&quot;&gt;Pattern matching&lt;a class=&quot;zola-anchor&quot; href=&quot;#pattern-matching&quot; aria-label=&quot;Anchor link for: pattern-matching&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Those are amazing, like a switch but way more powerful.
To handle something that could fail like the Go example above, it would look like this.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Example from docs with a few more stuff added
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;divide&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;numerator&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;denominator&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;f64&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; denominator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0.0 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;None
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(numerator &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt; denominator)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; The return value of the function is an option
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;divide&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;3.0&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Pattern match to retrieve the value
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; result {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Result was exactly 1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Some&lt;&#x2F;span&gt;&lt;span&gt;(x) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Result: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, x),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;None    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Cannot divide by 0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_ =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Nothing&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Compiler will fail and tell that this is unreachable as you&amp;#39;re dealing with Some and None
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A playpen is available &lt;a href=&quot;http:&#x2F;&#x2F;is.gd&#x2F;0tW2Bc&quot;&gt;here&lt;&#x2F;a&gt;, modify the paraneters to divide and the pattern matching to see how it works.
You could match on types, on values, this makes the code really neat imo and is awesome.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; 10i;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; x {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;It&amp;#39;s 0!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;9 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Between 1 and 9: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, x),
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;_ =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Different from 0-9: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, x) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; needed to be exhaustive here, compiler will yell without it
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;general-functional-functions&quot;&gt;General &#x27;functional&#x27; functions&lt;a class=&quot;zola-anchor&quot; href=&quot;#general-functional-functions&quot; aria-label=&quot;Anchor link for: general-functional-functions&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;I did the first Euler problems in Rust to get the hang of it and everytime I started with the naive approach of looping.
After reading a bit of code from experience devs, I realised you could do some nice stuff like defining a Fibonacci iterator and making a simple one liner after that to solve &lt;a href=&quot;https:&#x2F;&#x2F;projecteuler.net&#x2F;problem=2&quot;&gt;problem 2&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;Fibonacci::new().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;take_while&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;| i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;4000000&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;| i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;% &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;what-do-i-think-of-it&quot;&gt;What do I think of it?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-do-i-think-of-it&quot; aria-label=&quot;Anchor link for: what-do-i-think-of-it&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As you saw from the previous part, I&#x27;m quite enthusiastic about it.
It&#x27;s still too much in flux to be used seriously but I think that it could become a pretty big thing.
Again, talking from the point of view of websites&#x2F;services, it could become a nice alternative to Go (as more and more companies start using it for services).
The fact that they&#x27;re also building a package manager (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&quot;&gt;Cargo&lt;&#x2F;a&gt;) along the language is a very nice thing, solving the whole dependencies issues in Go, I need to find out whether they plan to include vendoring or not.
I think Go is easier to pick up (language has less features, some people will consider that a minus, other a plus), has the community behind it and tools like gofmt (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pcwalton&#x2F;rustfmt&quot;&gt;one&lt;&#x2F;a&gt; is in the works for Rust).
Another issue Rust currently has is that it is changing very fast, most of the threads&#x2F;posts talking about it will not work in the current version and that is confusing.
Up to date resources includes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;http:&#x2F;&#x2F;rustbyexample.com&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;http:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;index.html (very helpful since there&#x27;s no autocompletion yet, there&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;phildawes&#x2F;racer&quot;&gt;racer&lt;&#x2F;a&gt; but it doesn&#x27;t seem finished)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Overall, I&#x27;ll keep an eye on it and try to do a small project on it as soon as I have some free time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Python wheels</title>
          <pubDate>Sat, 24 May 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/using-python-wheel/</link>
          <guid>https://www.vincentprouillet.com/blog/using-python-wheel/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/using-python-wheel/">&lt;p&gt;To quote &lt;a href=&quot;http:&#x2F;&#x2F;legacy.python.org&#x2F;dev&#x2F;peps&#x2F;pep-0427&#x2F;&quot;&gt;PEP 427&lt;&#x2F;a&gt;, wheel is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A wheel is a ZIP-format archive with a specially formatted file name and the .whl extension. It contains a single distribution nearly as it would be installed according to PEP 376 with a particular installation scheme. Although a specialized installer is recommended, a wheel file may be installed by simply unpacking into site-packages with the standard &#x27;unzip&#x27; tool while preserving enough information to spread its contents out onto their final paths at any later time.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To put it simply, wheels are a way to have your own local instant pypi.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-should-i-care&quot;&gt;Why should I care&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-should-i-care&quot; aria-label=&quot;Anchor link for: why-should-i-care&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I actually read a few articles on it some time ago but didn&#x27;t see the usecase before actually using it myself.&lt;&#x2F;p&gt;
&lt;p&gt;Here are a few concrete examples on how you could possibly use them:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;instant installation on localhost&lt;&#x2F;strong&gt;: having a global wheel directory where you can pull your libs from without going to pypi and doing so almost instantly, even for libs requiring compilation (lxml, pyscopg2, pillow, etc)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;deploying without needing pypi&lt;&#x2F;strong&gt;: obviously the compiled libs won&#x27;t work if you are using different systems for developing and in production but the rest will work&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;WAY faster installation of requirements&lt;&#x2F;strong&gt;: it takes only a few seconds to install, compared to a possibly long time depending on what you are using. Think of the time your builds on CI are taking just to install the requirements.&lt;&#x2F;li&gt;
&lt;li&gt;maybe deploy your project as a wheel? I haven&#x27;t thought about it too much but that &lt;em&gt;should&lt;&#x2F;em&gt; be possible and you could change version using pip, the downside being that your project would be in the site-packages directory so it would be a bit annoying for management commands for django fo example&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I don&#x27;t mind waiting a bit for my pip install but the deploy without pypi and the faster builds are killer features in my eyes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-use-them&quot;&gt;How to use them&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-use-them&quot; aria-label=&quot;Anchor link for: how-to-use-them&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s imagine you have a project you want to convert to using wheels.
You would have your dependencies in a requirements.txt (or several requirements file for different environments).
The first thing you need to do is installing wheel.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; pip install wheel &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# with pip version &amp;gt;= 1.4
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point you remember that not even 10 lines above, I said we didn&#x27;t use to use pypi to deploy.
Wheels are zip archives that python can execute so that means we can actually use the wheel of wheel to install wheel (yo dawg, I heard you liked wheels).
You could download the wheel manually and installing using whatever deployment system you are using but let&#x27;s install from pip locally.&lt;&#x2F;p&gt;
&lt;p&gt;Now that you got pip and your requirements file ready, time to reinvent the wheels (haha...).
For example, this is the result of running it in my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;django-drf-template&quot;&gt;Django REST Framework template&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; pip wheel&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --wheel-dir&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;wheelhouse&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; -r&lt;&#x2F;span&gt;&lt;span&gt; requirements&#x2F;local.txt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A ls in wheelhouse gives me the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;bcrypt-1.0.2-cp27-none-linux_x86_64.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;docutils-0.11-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;pep8-1.5.6-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;cffi-0.8.2-cp27-none-linux_x86_64.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;flake8-2.1.0-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;psycopg2-2.5.3-cp27-none-linux_x86_64.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Django-1.7b4-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ipdb-0.8-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;pycparser-2.10-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;django_cors_headers-0.12-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ipython-2.1.0-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;pyflakes-0.8.1-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;django_debug_toolbar-1.2.1-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Jinja2-2.7.2-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Pygments-1.6-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;django_extensions-1.3.7-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Markdown-2.4-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;six-1.6.1-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;django_model_utils-2.0.3-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;MarkupSafe-0.23-cp27-none-linux_x86_64.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Sphinx-1.2.2-py27-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;djangorestframework-2.3.13-py2.py3-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;mccabe-0.2.1-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;sqlparse-0.1.11-py2-none-any.whl
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This shows pretty well the format of wheel filenames:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The platform tag part is important, as a wheel compiled on mac will not work on an Ubuntu machine for example.&lt;&#x2F;p&gt;
&lt;p&gt;To install my requirements from my wheels, I simply need to tell pip where to look for:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; pip install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --use-wheel --no-index --find-links&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;wheelhouse&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; -r&lt;&#x2F;span&gt;&lt;span&gt; requirements&#x2F;local.txt
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;no-index&lt;&#x2F;code&gt; part flag ensures that pip won&#x27;t try to look it up in others sources than the directory we specify in find-links.
Delete your virtualenv and see how fast it is to install now (apparently wheel ignores URLs though, you could install the wheel file manually as explained above).&lt;&#x2F;p&gt;
&lt;p&gt;This is roughly the workflow with using wheels for your project.&lt;&#x2F;p&gt;
&lt;p&gt;Wheels definitely look like a win for every environment:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;dev: instant installation of most of your pip packages across your projects if you are using a shared wheel directory&lt;&#x2F;li&gt;
&lt;li&gt;ci: faster build and not reliant on pypi&lt;&#x2F;li&gt;
&lt;li&gt;prod: not reliant on pypi to deploy&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>A template for Django REST Framework projects</title>
          <pubDate>Thu, 15 May 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/making-a-django-rest-framework-template/</link>
          <guid>https://www.vincentprouillet.com/blog/making-a-django-rest-framework-template/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/making-a-django-rest-framework-template/">&lt;p&gt;I made a project template for Django 1.7 + Django REST framework + PostgreSQL containing things I use and find useful.
Part of it is based on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;twoscoops&#x2F;django-twoscoops-project&quot;&gt;Two Scoops of Django template&lt;&#x2F;a&gt;.
You can see the repo on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;django-drf-template&quot;&gt;Github&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;features&quot;&gt;Features&lt;a class=&quot;zola-anchor&quot; href=&quot;#features&quot; aria-label=&quot;Anchor link for: features&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It contains the following things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.django-rest-framework.org&#x2F;&quot;&gt;Django REST framework&lt;&#x2F;a&gt;: for writing your API&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;django-model-utils.readthedocs.org&#x2F;en&#x2F;latest&#x2F;&quot;&gt;django-model-utils&lt;&#x2F;a&gt;: for things like TimestampedModel and other nice things for models&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ottoyiu&#x2F;django-cors-headers&quot;&gt;django-cors-headers&lt;&#x2F;a&gt;: automatically allows CORS on local dev and allows for a whitelist in production&lt;&#x2F;li&gt;
&lt;li&gt;bcrypt: hash password with bcrypt instead of PBKDF2&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are also goodies on dev and test environments:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;django-debug-toolbar&#x2F;django-debug-toolbar&quot;&gt;django-debug-toolbar&lt;&#x2F;a&gt;: lots of information on request&#x2F;response and queries&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;ipdb&quot;&gt;ipdb&lt;&#x2F;a&gt;: pdb in ipython. Awesome&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;django-extensions&#x2F;django-extensions&quot;&gt;django-extensions&lt;&#x2F;a&gt;: adds some useful management commands&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;nedbatchelder.com&#x2F;code&#x2F;coverage&#x2F;&quot;&gt;coverage&lt;&#x2F;a&gt;: measures code coverage&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;factoryboy.readthedocs.org&#x2F;en&#x2F;latest&#x2F;&quot;&gt;factory-boy&lt;&#x2F;a&gt;: my library of choice to create objects in tests, I go more into details in my article &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;factory-boy-or-model-mommy&#x2F;&quot;&gt;Python testing: Factory Boy or Model Mommy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;flake8.readthedocs.org&quot;&gt;flake8&lt;&#x2F;a&gt;: ensures code quality, respect of pep8 and other nice things, it can easily be installed as a git&#x2F;mercurial hook&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;install&quot;&gt;Install&lt;a class=&quot;zola-anchor&quot; href=&quot;#install&quot; aria-label=&quot;Anchor link for: install&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You will need Postgres installed and the following libs (for ubuntu&#x2F;debian, for others systems look in your package managers).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; sudo apt-get install libpq-dev python-dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Create your virtualenv (and virtualenvwrapper in the example case), I will use the name myproject but use your own name.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; mkdir myproject &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span&gt; myproject
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; mkvrirtualenv myproject
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; pip install django
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; django-admin.py startproject myproject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --template&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;django-drf-template&#x2F;archive&#x2F;master.zip
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; cd myproject
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; pip install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; -r&lt;&#x2F;span&gt;&lt;span&gt; requirements&#x2F;local.txt
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; python myproject&#x2F;manage.py migrate
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want, you can also add a pre-commit flake8 hook to ensure that commit respects it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; flake8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --install-hook
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By default it will not stop commits because of warning, a quick look at .git&#x2F;hooks&#x2F;pre-commit shows that putting an environment variable of FLAKE8_STRICT will stop them.&lt;&#x2F;p&gt;
&lt;p&gt;And you should be almost good to go.
There are a few hardcoded temporary settings that you will want to replace, look for the string &#x27;Ann Onymous&#x27; and you should find them.
They are in the Sphinx folder and in the base settings file if you&#x27;re lazy.&lt;&#x2F;p&gt;
&lt;p&gt;Once again, PRs are welcome if there&#x27;s a bug or want to share something you use all the time that I don&#x27;t necessarily know about !&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Switching to TypeScript</title>
          <pubDate>Sun, 04 May 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/switching-to-typescript/</link>
          <guid>https://www.vincentprouillet.com/blog/switching-to-typescript/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/switching-to-typescript/">&lt;p&gt;Until a few weeks ago, I was a convinced user of coffeescript: significant whitespace, no curly braces, list comprehensions, classes, thick arrow, chained comparisons, etc.
The list of good things coffeescript brings to the table is quite long, just have a look the &lt;a href=&quot;http:&#x2F;&#x2F;coffeescript.org&#x2F;&quot;&gt;coffeescript website&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Then, Microsoft announed &lt;a href=&quot;http:&#x2F;&#x2F;www.typescriptlang.org&#x2F;&quot;&gt;Typescript 1.0&lt;&#x2F;a&gt; and that made me give a second look to it.&lt;&#x2F;p&gt;
&lt;p&gt;My initial reaction the first time I heard about it was something along the line of: eh, they are probably going to stop working on it in a year or so, like lots of their projects.
Turns out I was wrong !
I am not going to detail Typescript too much as there is a complete &lt;a href=&quot;http:&#x2F;&#x2F;www.typescriptlang.org&#x2F;Handbook&quot;&gt;handbook&lt;&#x2F;a&gt; available.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-would-i-use-typescript&quot;&gt;Why would I use Typescript ?&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-would-i-use-typescript&quot; aria-label=&quot;Anchor link for: why-would-i-use-typescript&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I can see several reasons.&lt;&#x2F;p&gt;
&lt;p&gt;First, it&#x27;s still mostly javascript, unlike coffeescript which makes it easier to learn (coffeescript is very easy but lots of people will not contribute back if it means having to do it in coffeescript).
There is a big thread on Discourse (link now dead) about whether to use Javascript or Coffeescript.
After switching to javascript, more contributions happened so that&#x27;s definitely something to take into account if you are doing an open source project.&lt;&#x2F;p&gt;
&lt;p&gt;The second is obviously types. Having types (in javascript) is awesome.
All the typescript type checking only happens for the developer, nothing will be left in the compiled js (which is perfectly readable javascript by the way).
Having types also mean that you can have autocompletion available, even for your custom types, which is pretty cool.
Keep in mind that types are optional, no one forces you to use them if you don&#x27;t want to.
If you decide to use them though, a vital resource for Typescript is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;borisyankov&#x2F;DefinitelyTyped&quot;&gt;DefinitelyTyped&lt;&#x2F;a&gt; which contains definitions for most of the JS libraries.&lt;&#x2F;p&gt;
&lt;p&gt;The third would be all the other features that Typescript brings: modules, classes, arrow functions, etc.
Their goal is to have an interface similar to what we will have in ES6 but, as ES6 is still changing a lot (for example the module implementation), it can differ quite a bit right now.
I am confident they will adapt to whatever standard has been adopted, unless they decide to somehow abandon their goal of sticking to ES6.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-am-i-using-it&quot;&gt;How am I using it&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-am-i-using-it&quot; aria-label=&quot;Anchor link for: how-am-i-using-it&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I finished switching my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&quot;&gt;ng-boilerplate&lt;&#x2F;a&gt; to Typescript and it works pretty well.
Since I use AngularJS with REST APIs, I can define the types I will receive in the API, methods on controllers&#x2F;scopes and have some robust code that ensure I didn&#x27;t call a made-up function or made a typo in an object attribute immediately.
The interfaces you define can also serve as a very good documentation and helps if you&#x27;re dealing with a huge codebase (which is the goal of Typescript).
I made a simple invoice app (github &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;invoicer&quot;&gt;link&lt;&#x2F;a&gt;) when I was trying it out and it caught many errors before I even opened the page.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While Typescript is not the Holy Grail of Javascript, it makes writing it as nice as coffeescript (coffeescript will be more concise imo though) while having types.
I can see it working very well once a project gets big but I would use it even for small projects.
Try it out and see for yourself !&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Python testing: Factory Boy or Model Mommy</title>
          <pubDate>Sat, 05 Apr 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/factory-boy-or-model-mommy/</link>
          <guid>https://www.vincentprouillet.com/blog/factory-boy-or-model-mommy/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/factory-boy-or-model-mommy/">&lt;p&gt;Writing tests is necessary.
Whether you write them before the code itself (TDD, BDD) or after, it allows you to refactor and change your code with confidence (nobody wants to refactor a critical feature with no coverage).
One thing you need very frequently in tests is objects.
There&#x27;s several choices when it comes to how you can create objects for your tests:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Fixtures: objects are stored as json or another filetype and loaded for tests. This is a bad idea as you need to update your fixtures everytime you modify your models&lt;&#x2F;li&gt;
&lt;li&gt;ORM: you could for example use MyModel.objects.create in Django to create your object in the database. Much cleaner than fixtures but you will repeat yourself quite a bit.&lt;&#x2F;li&gt;
&lt;li&gt;Factories: that&#x27;s what the article is about, define a template once (or not at all with Model Mommy) and you can use the factory in all your tests.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You could create your own factory system based on the ORM but there are libraries that already do that, and do it well so there&#x27;s no point reinventing the wheel.
All the examples below will be for Django tests and we are going to make factories for the following models:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# This is pseudocode, in reality that would be your django or SQLAlchemy models
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Teashop&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;string
&lt;&#x2F;span&gt;&lt;span&gt;    address &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;string
&lt;&#x2F;span&gt;&lt;span&gt;    owner &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;Owner
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Owner&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    first_name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;string
&lt;&#x2F;span&gt;&lt;span&gt;    last_name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;string
&lt;&#x2F;span&gt;&lt;span&gt;    email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;string
&lt;&#x2F;span&gt;&lt;span&gt;    age &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;factory-boy&quot;&gt;Factory Boy&lt;a class=&quot;zola-anchor&quot; href=&quot;#factory-boy&quot; aria-label=&quot;Anchor link for: factory-boy&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;is based on factory_girl for those of you that used it in Ruby.
Using it is very simple, define your factory by creating a class that inherits from the class you want to use (it supports several ORM, mainly Django and SQLAlchemy).
It brings lots of cool features like lazy attributes, fuzzy attributes,  sequences and more.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how the factories would work for our models:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;OwnerFactory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;factory.django.DjangoModelFactory&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Meta&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        model &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;models.Owner
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    first_name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;bobby&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    last_name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;D&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# age will be somewhere between 25 and 42
&lt;&#x2F;span&gt;&lt;span&gt;    age &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;FuzzyInteger&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# email will use first_name and last_name (the default or the one you provide)
&lt;&#x2F;span&gt;&lt;span&gt;    email &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;factory.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;LazyAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{0}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{1}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;@example.com&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span&gt;(a.first_name, a.last_name).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;lower&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;TeashopFactory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;factory.django.DjangoModelFactory&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Meta&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        model &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;models.Teashop
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Tea-Bone&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# the first teashop will be 0 Downing street, the second 1 Downing Street etc
&lt;&#x2F;span&gt;&lt;span&gt;    address &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;factory.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Sequence&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{0}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt; Downing Street&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;format&lt;&#x2F;span&gt;&lt;span&gt;(n))
&lt;&#x2F;span&gt;&lt;span&gt;    owner &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;factory.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;SubFactory&lt;&#x2F;span&gt;&lt;span&gt;(OwnerFactory)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, to use it in your tests, it&#x27;s very simple:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;OwnerFactory&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# will save into database and return an instance of the model
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;OwnerFactory.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;create&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# same as above
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;OwnerFactory.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# will create the object but not save it in database, very cool for unit tests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;OwnerFactory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;first_name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Malcom&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# override the default first name we defined in the factory
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;TeashopFactory&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# will create both a teashop and an associated owner model and return the teashop
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;TeashopFactory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;owner&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;my_owner_object) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# will use the Owner object provided instead of creating one
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Factory boy also provides hooks if you need to modify the object or save some properties in another database.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;model-mommy&quot;&gt;Model Mommy&lt;a class=&quot;zola-anchor&quot; href=&quot;#model-mommy&quot; aria-label=&quot;Anchor link for: model-mommy&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vandersonmota&#x2F;model_mommy&quot;&gt;Model Mommy&lt;&#x2F;a&gt; is an extremely simple way to create objects for tests in Django (and Django only).
Look at that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;mommy.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(Owner) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# identical to .create() in factory boy
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;mommy.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(Teashop) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# will create and save the teashop and the owner
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;mommy.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;prepare&lt;&#x2F;span&gt;&lt;span&gt;(Owner) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# create but not save in the database
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And yes, there&#x27;s nothing else to define. Model mommy will automatically fill the objects attributes with random data corresponding to the type of column in the ORM.
You can obviously define the attributes manually if you want, even for related objects:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;mommy.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(Owner, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;first_name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Malcolm&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;mommy.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(Teashop, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;owner__first_name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;Malcom&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# AWESOME
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;mommy.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(Teashop, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_quantity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# creates and return 7 teashop objects, everyone gets a teashop!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You might be thinking that having random data in your tests can be a bad idea (I tend to agree) and annoying to track down bugs (it&#x27;s hard to see that sdawrewaev is a first name).
Thankfully, Model Mommy provides a way to set predefined data called recipes, with several examples in the https:&#x2F;&#x2F;github.com&#x2F;vandersonmota&#x2F;model_mommy#recipes (link now dead).
It doesn&#x27;t have lazy attributes though.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;which-one-should-i-use&quot;&gt;Which one should I use?&lt;a class=&quot;zola-anchor&quot; href=&quot;#which-one-should-i-use&quot; aria-label=&quot;Anchor link for: which-one-should-i-use&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you are using something other than Django, the choice is easy and you should use Factory Boy if possible.
If you are using Django, things are pretty much down to your personal preference as both do a good job providing models for your tests.
I really like how simple it is to use Model Mommy and the ORM syntax to edit relationship attributes but since I don&#x27;t want random data, I would always use recipes and I might as well use Factory Boy.
In the end I use Factory Boy, the explicit declaration and the possibility of using the same tool on a Flask or Pyramid project makes it very attractive.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Updating my AngularJS boilerplate</title>
          <pubDate>Wed, 26 Mar 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/updating-my-angular-boilerplate/</link>
          <guid>https://www.vincentprouillet.com/blog/updating-my-angular-boilerplate/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/updating-my-angular-boilerplate/">&lt;p&gt;I made a post about starting an angular project a few months ago (&lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;starting-an-angularjs-project&#x2F;&quot;&gt;there&lt;&#x2F;a&gt;) and I kept updating the boilerplate to adapt to my workflow.
Here&#x27;s what&#x27;s different from that time (spoiler: mostly the build system).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structure&quot;&gt;Structure&lt;a class=&quot;zola-anchor&quot; href=&quot;#structure&quot; aria-label=&quot;Anchor link for: structure&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The structure is very similar to what I had before:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; app
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── app.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── home
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── module.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; assets
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── img
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── test.png
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; index.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; style
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── app.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── _foundation.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── _grid-settings.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── snowflake
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; templates
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── home
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── index.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; tests
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; integration
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── home
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── basic.tests.coffee
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; unit
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; home
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; basic.tests.coffee
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you compare with the previous version, you can notice it changed a bit.
The templates, tests and styles are placed in their own directory (with tests being separated by unit and integration) since I personally find it cleaner that way.
I&#x27;ve seen quite a few people mentionning they were putting all of this in the feature directory but to me it makes it hard to find stuff (I use the same templates and tests directory in my django projects as well so it may be an habit).&lt;&#x2F;p&gt;
&lt;p&gt;In the style directory, you might have noticed the snowflake thing.
This is my base SCSS (you can see the repo on github: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;snowflake&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;snowflake&lt;&#x2F;a&gt;) which is Bourbon (for the mixins you would usually use compasss), Neat (for the grid system) and Bitters (for the typography for now).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;build-and-features&quot;&gt;Build and features&lt;a class=&quot;zola-anchor&quot; href=&quot;#build-and-features&quot; aria-label=&quot;Anchor link for: build-and-features&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The previous build system was using Grunt but I switched to Gulp as the config file made much more sense to me (and way simpler to understand and faster).
The build does the same thing the Grunt was doing and a few additional things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;watch over .coffee file and compile them&lt;&#x2F;li&gt;
&lt;li&gt;watch over .sass file and compile them&lt;&#x2F;li&gt;
&lt;li&gt;watch over .html file and minify them (wasn&#x27;t there before)&lt;&#x2F;li&gt;
&lt;li&gt;watch over the images in the assets directory and copy them when added&#x2F;modified&lt;&#x2F;li&gt;
&lt;li&gt;reload the page when any of this is happening&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Another thing I added is Protractor (end to end testing using webdrivers) support.
By default, it does not run on the watch task as it can take some time but a CI task that run both unit and integration tests is available (and runs on travis with the .travis.yml file provided).
If you have an opensource project on github, Travis CI works out of the box.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m pretty happy with the current setup, the only thing I&#x27;m thinking of is to use coffeescript classes a bit more instead of functions for controllers and services but I haven&#x27;t explored that too much yet.
Another thing I do on my own project but didn&#x27;t do in that boilerplate is checking in the node_modules and libs directory. I do that in order to not have failures on CI or on deploy due to internet or npm issues but I prefer giving people the choice and keeping the repo small so it&#x27;s still gitignored for now.&lt;&#x2F;p&gt;
&lt;p&gt;Any feedback is obviously much appreciated !&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Gulp by example</title>
          <pubDate>Mon, 17 Feb 2014 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/introducing-people-to-gulp/</link>
          <guid>https://www.vincentprouillet.com/blog/introducing-people-to-gulp/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/introducing-people-to-gulp/">&lt;h4 id=&quot;update-2014-07-23&quot;&gt;UPDATE (2014&#x2F;07&#x2F;23)&lt;a class=&quot;zola-anchor&quot; href=&quot;#update-2014-07-23&quot; aria-label=&quot;Anchor link for: update-2014-07-23&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;I have updated gulp and replaced connect with browsersync (I did that already in the article with an earlier version of browser-sync but not on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&quot;&gt;example project&lt;&#x2F;a&gt; on github, it is now up to date).
I also do not use coffeescript anymore, I &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;switching-to-typescript&#x2F;&quot;&gt;switched to typescript&lt;&#x2F;a&gt; and use plain js for the gulpfile but everything in this post is still valid.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;update-2015-05-07&quot;&gt;UPDATE (2015&#x2F;05&#x2F;07)&lt;a class=&quot;zola-anchor&quot; href=&quot;#update-2015-05-07&quot; aria-label=&quot;Anchor link for: update-2015-05-07&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;This article was written for gulp 3.
If you want to move to gulp 4, please go &lt;a href=&quot;https:&#x2F;&#x2F;www.vincentprouillet.com&#x2F;blog&#x2F;migrating-to-gulp4&#x2F;&quot;&gt;this article&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So I wanted to make changes to my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&quot; title=&quot;ng-boilerplate&quot;&gt;ng-boilerplate&lt;&#x2F;a&gt; project to make it simple enough for a javascript newbie to understand what was going on.
The main issue was the Gruntfile and its configuration, roughly 400 lines of javascript handling all the possible tasks (watch, releasse, test, etc): it was working, but was pretty hard to see what was going on.
Also having to put files in temporary directory all the time was bugging me.
Enter &lt;a href=&quot;http:&#x2F;&#x2F;gulpjs.com&#x2F;&quot; title=&quot;gulp.js&quot;&gt;gulp.js&lt;&#x2F;a&gt; (an example project following the example described in this post is available &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&quot; title=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&quot;&gt;here&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gulp&quot;&gt;Gulp&lt;a class=&quot;zola-anchor&quot; href=&quot;#gulp&quot; aria-label=&quot;Anchor link for: gulp&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Gulp uses Node.js streams (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;substack&#x2F;stream-handbook&quot; title=&quot;stream handbook&quot;&gt;explanations here&lt;&#x2F;a&gt;), meaning it doesn&#x27;t need to create files and thus is faster than Grunt.
Another advantage for Gulp is that gulpfiles are actually code, not configuration like Grunt, making it very easy to understand what&#x27;s going on at a glance (and shorter in my case as well).
The way gulp works is the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;select the input files (for example all the .coffee files)&lt;&#x2F;li&gt;
&lt;li&gt;pass them through plugins (linting, coffeescript, concat, minify)&lt;&#x2F;li&gt;
&lt;li&gt;output them somewhere if needed&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Contrary to grunt, you only define the source files once, not for every plugin.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example&quot;&gt;Example&lt;a class=&quot;zola-anchor&quot; href=&quot;#example&quot; aria-label=&quot;Anchor link for: example&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I am going to use a basic sass&#x2F;coffeescript project setup for the example.
The project is organised the following way:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; dist
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── css
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── index.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── js
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; gulpfile.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; index.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; package.json
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; README.md
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; sass
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;.scss
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The goal is to have automatic compilation of sass and copy of index.html and *.coffee files to the dist folder on change, automatically reloading the page when that happens.
First we need to install gulp and create a gulpfile (in coffeescript for the example).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; sudo npm install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; -g&lt;&#x2F;span&gt;&lt;span&gt; gulp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# for the cli
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npm install gulp gulp-util coffee-script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --save-dev
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; touch gulpfile.coffee
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can run a gulp task that way:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gulp mytaskname
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gulp
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The second command will run the task called default.&lt;&#x2F;p&gt;
&lt;p&gt;Before modifying the file, let&#x27;s think at what we will need:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;sass compiler (gulp-sass)&lt;&#x2F;li&gt;
&lt;li&gt;something to reload the page (browsersync)&lt;&#x2F;li&gt;
&lt;li&gt;coffeescript linter (gulp-coffeelint)&lt;&#x2F;li&gt;
&lt;li&gt;coffeescript compiler (gulp-coffee)&lt;&#x2F;li&gt;
&lt;li&gt;concat files (gulp-concat)&lt;&#x2F;li&gt;
&lt;li&gt;minify js (gulp-uglify)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s install those.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npm install gulp-sass browser-sync gulp-coffeelint gulp-coffee gulp-concat gulp-uglify&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --save-dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No need to load task the Grunt way, we can just require those the node way:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;gulp = require &amp;#39;gulp&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;gutil = require &amp;#39;gulp-util&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;sass = require &amp;#39;gulp-sass&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;browserSync = require &amp;#39;browser-sync&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;coffeelint = require &amp;#39;gulp-coffeelint&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;coffee = require &amp;#39;gulp-coffee&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;concat = require &amp;#39;gulp-concat&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;uglify = require &amp;#39;gulp-uglify&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I like to define the sources&#x2F;destinations paths right at the beginning, works for me but everyone will do that differently.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;sources =
&lt;&#x2F;span&gt;&lt;span&gt;  sass: &amp;#39;sass&#x2F;**&#x2F;*.scss&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  html: &amp;#39;index.html&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  coffee: &amp;#39;src&#x2F;**&#x2F;*.coffee&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;destinations =
&lt;&#x2F;span&gt;&lt;span&gt;  css: &amp;#39;dist&#x2F;css&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  html: &amp;#39;dist&#x2F;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  js: &amp;#39;dist&#x2F;js&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Gulp has a very simple API and we are going to use 4 methods from it: task, src, dest and watch (that&#x27;s pretty much the whole API).&lt;&#x2F;p&gt;
&lt;p&gt;The first task will be to set the server for the dev environment with the autoreload.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;# Reloads the page for us
&lt;&#x2F;span&gt;&lt;span&gt;gulp.task &amp;#39;browser-sync&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    browserSync.init null,
&lt;&#x2F;span&gt;&lt;span&gt;    open: false
&lt;&#x2F;span&gt;&lt;span&gt;    server:
&lt;&#x2F;span&gt;&lt;span&gt;      baseDir: &amp;quot;.&#x2F;dist&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    watchOptions:
&lt;&#x2F;span&gt;&lt;span&gt;      debounceDelay: 1000
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are several plugins that reload the page but this one is the simplest I found, simply defines the files you want to watch and it will reload for you, no need for additional config.
This task is not a really good example of what gulp does so let&#x27;s move to a more exciting one, the sass task.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;gulp.task &amp;#39;style&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.src(sources.sass) # we defined that at the top of the file
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(sass({outputStyle: &amp;#39;compressed&amp;#39;, errLogToConsole: true}))
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(gulp.dest(destinations.css))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you can see a bit more of a magic but it&#x27;s still fairly straightforward.
gulp.src finds the files that matches the glob, pipe them to the sass plugin that will compile them (setting errLogToConsole to true means we won&#x27;t exit gulp if we make a mistake in the sass file, good when watching), result is piped to gulp.dest which defines the destination to which we want to write the file.&lt;&#x2F;p&gt;
&lt;p&gt;The HTML task just copies the index.html file to the dist folder and reloads the server, you should be able to follow the code by now.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;gulp.task &amp;#39;html&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.src(sources.html)
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(gulp.dest(destinations.html))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now the coffeescript task is more interesting because it really highlights the difference between Grunt and Gulp.
With Grunt you would need to put your files into temporary folders, for example after compiling the .coffee files.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;# I put linting as a separate task so we can run it by itself if we want to
&lt;&#x2F;span&gt;&lt;span&gt;gulp.task &amp;#39;lint&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.src(sources.coffee)
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(coffeelint())
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(coffeelint.reporter())
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;gulp.task &amp;#39;src&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.src(sources.coffee)
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(coffee({bare: true}).on(&amp;#39;error&amp;#39;, gutil.log))
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(concat(&amp;#39;app.js&amp;#39;))
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(uglify())
&lt;&#x2F;span&gt;&lt;span&gt;  .pipe(gulp.dest(destinations.js))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I find this system quite brilliant, no need to go through temp folders just to be able to run all your tasks and it&#x27;s WAY more readable than going through the config for each of these plugins you would do in Grunt.&lt;&#x2F;p&gt;
&lt;p&gt;The last bit to do is the watch task, ie the one used in dev to do all these things when a file changes.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;gulp.task &amp;#39;watch&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.watch sources.sass, [&amp;#39;style&amp;#39;]
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.watch sources.app, [&amp;#39;lint&amp;#39;, &amp;#39;src&amp;#39;, &amp;#39;html&amp;#39;]
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.watch sources.html, [&amp;#39;html&amp;#39;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  # And we reload our page if something changed in the output folder
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.watch &amp;#39;dist&#x2F;**&#x2F;**&amp;#39;, (file) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    browserSync.reload(file.path) if file.type is &amp;quot;changed&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it, you got the whole thing set up (well...), very easy to understand even by someone that never used gulp.
Any dev should be able to tell what&#x27;s going on from the code itself.&lt;&#x2F;p&gt;
&lt;p&gt;I lied a bit when I said everything was setup.
Right now there are 2 glaring issues:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;no way to make a clean build&lt;&#x2F;li&gt;
&lt;li&gt;we don&#x27;t want to have the js minified when developing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s fix that !
We need to install a few additional plugins and add a few tasks:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; npm install gulp-clean run-sequence&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --save-dev
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;gulp.task &amp;#39;clean&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  gulp.src([&amp;#39;dist&#x2F;&amp;#39;], {read: false}).pipe(clean())
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;gulp.task &amp;#39;build&amp;#39;, -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  runSequence &amp;#39;clean&amp;#39;, [&amp;#39;style&amp;#39;, &amp;#39;lint&amp;#39;, &amp;#39;src&amp;#39;, &amp;#39;html&amp;#39;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;gulp.task &amp;#39;default&amp;#39;, [&amp;#39;build&amp;#39;, &amp;#39;browser-sync&amp;#39;,&amp;#39;watch&amp;#39;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This allows us to make a clean build and watch over the changes.
For the env&#x2F;prod differences (like minifying), you need to pass another argument called type when running gulp:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gulp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --type&lt;&#x2F;span&gt;&lt;span&gt; prod
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then retrieve that value using gulp-util and use it in the tasks:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;isProd = gutil.env.type is &amp;#39;prod&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;# Do your if statements in the tasks
&lt;&#x2F;span&gt;&lt;span&gt;.pipe(if isProd then uglify() else gutil.noop())
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can find an example app using this gulpfile here &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&quot; title=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&lt;&#x2F;a&gt;, and see the whole gulpfile &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;gulp-example&#x2F;blob&#x2F;master&#x2F;gulpfile.coffee&quot; title=&quot;finished gulpfile&quot;&gt;there&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I really like Gulp !
This feels way more simple to write and to read than Grunt was.
I got my ng-boilerplate setup to be equal to how it was with Grunt in one afternoon and it certainly feels faster.
If you have any comments or feedback (ie I&#x27;m doing something horribly wrong), feel free to post a comment and&#x2F;or make a pull request to the example project.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Making third party widgets</title>
          <pubDate>Wed, 06 Nov 2013 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/making-third-party-widgets/</link>
          <guid>https://www.vincentprouillet.com/blog/making-third-party-widgets/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/making-third-party-widgets/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;By third-party widgets, I mean javascript that you load on an external website and
that talks to your website, think Facebook Like button or Disqus comments.
Since I didn&#x27;t find a lot of resources explaining how to create them (except the excellent book simply
named &lt;a href=&quot;https:&#x2F;&#x2F;www.manning.com&#x2F;books&#x2F;third-party-javascript&quot; title=&quot;Third-party JavaScript&quot;&gt;Third-party JavaScript&lt;&#x2F;a&gt;), I will give some details on how I did the widgets for
&lt;a href=&quot;https:&#x2F;&#x2F;www.playfire.com&#x2F;&quot; title=&quot;Playfire&quot;&gt;Playfire&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;a class=&quot;zola-anchor&quot; href=&quot;#examples&quot; aria-label=&quot;Anchor link for: examples&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here are some examples of the widgets I made:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;disqus-like comments on the playfire blog: example &lt;a href=&quot;http:&#x2F;&#x2F;blog.playfire.com&#x2F;2013&#x2F;11&#x2F;bf-vs-cod-which-do-you-want.html&quot; title=&quot;Link to blog article&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Want button, to add a game to their wishlist from the GreenManGaming website&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-theory&quot;&gt;The theory&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-theory&quot; aria-label=&quot;Anchor link for: the-theory&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s define some terms first.
The website where the widget is embedded is called a &lt;strong&gt;consumer&lt;&#x2F;strong&gt; while the website hosting is is called a &lt;strong&gt;provider&lt;&#x2F;strong&gt;.
Communicating between the consumer and the provider is the biggest problem you will meet early on.
There are a few ways to deal with it, I will only present the ones that are usable if you intent to send&#x2F;receive data with the widgets.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;iframe&quot;&gt;Iframe&lt;a class=&quot;zola-anchor&quot; href=&quot;#iframe&quot; aria-label=&quot;Anchor link for: iframe&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Iframe embeds another page in the page, avoiding completely the cross origin problem since the widget would actually be on domain.
The problem with using iframe like this is that iframes do not inherit styles from the main page, meaning it might look completely different
from the rest of the website. You can sniff the style and apply them but designing becomes tricky.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cors&quot;&gt;CORS&lt;a class=&quot;zola-anchor&quot; href=&quot;#cors&quot; aria-label=&quot;Anchor link for: cors&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;CORS (Cross Origin Resource Sharing) is an official spec designed to do exactly that (&lt;a href=&quot;http:&#x2F;&#x2F;www.html5rocks.com&#x2F;en&#x2F;tutorials&#x2F;cors&#x2F;&quot; title=&quot;Article about CORS&quot;&gt;this article&lt;&#x2F;a&gt;) explains it well).
If you don&#x27;t need to support older browsers (IE8 and IE9 have some &lt;a href=&quot;http:&#x2F;&#x2F;blogs.msdn.com&#x2F;b&#x2F;ieinternals&#x2F;archive&#x2F;2010&#x2F;05&#x2F;13&#x2F;xdomainrequest-restrictions-limitations-and-workarounds.aspx&quot; title=&quot;CORS in IE8-9&quot;&gt;issues&lt;&#x2F;a&gt;), CORS should be the way to go.
This will require the server to set appropriate headers to responses from the widgets.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hidden-iframes&quot;&gt;Hidden iframes&lt;a class=&quot;zola-anchor&quot; href=&quot;#hidden-iframes&quot; aria-label=&quot;Anchor link for: hidden-iframes&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This method will not use iframes directly.
Instead, as its name suggest, a hidden iframe will be created and will be used to transmit and receive data from the provider.
The widgets itself will be inserted in the page HTML normally.
This is the approach I used since it works on every browsers.&lt;&#x2F;p&gt;
&lt;p&gt;So now that you have decied on how to communicate between consumer and provider, let&#x27;s focus on the consumer part.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;consumer-side&quot;&gt;Consumer side&lt;a class=&quot;zola-anchor&quot; href=&quot;#consumer-side&quot; aria-label=&quot;Anchor link for: consumer-side&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The consumer will probably embed your widgets to add a feature to their website (social buttons, stats, ...) but they are most of the time
not the main part of a page.
It&#x27;s critical that your widget doesn&#x27;t slow down the consumer website or cause errors.
For the speed part, you need to load your script asynchronously otherwise you could block the rendering of the page.&lt;&#x2F;p&gt;
&lt;p&gt;P4W (Playfire 4 Web, name of the Playfire widgets) uses this to load:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;script&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;() {
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;p4w &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#a2a001;&quot;&gt;document&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;createElement&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#d07711;&quot;&gt;&amp;#39;script&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;p4w&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;.async &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;p4w&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;.src &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#d07711;&quot;&gt;&amp;#39;https:&#x2F;&#x2F;p4w.playfire.com&#x2F;embed.js&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;      (&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#a2a001;&quot;&gt;document&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;getElementsByTagName&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#d07711;&quot;&gt;&amp;#39;head&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#72ab00;&quot;&gt;|| &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#a2a001;&quot;&gt;document&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;getElementsByTagName&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#d07711;&quot;&gt;&amp;#39;body&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;)[&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;]).&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;appendChild&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#5597d6;&quot;&gt;p4w&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#b0b3ba14;color:#1f1f1f;&quot;&gt;    })();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&#x2F;script&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This assumes that the page has a &amp;lt;head&amp;amp; or &amp;lt;body&amp;amp; tag. A better way would be to find the first &amp;lt;script&amp;amp; (since we have at least one for sure) and insert the new script tag before it.
In this case embed.js is just a very small file that will load the real javascript file.
Keeping it simple here means that you can change whatever you want behind the scene, it will still works for the people using the above snippet in their website.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;provider-side&quot;&gt;Provider side&lt;a class=&quot;zola-anchor&quot; href=&quot;#provider-side&quot; aria-label=&quot;Anchor link for: provider-side&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Nothing particular needs to be done on the server since we are not using CORS.
If you are, don&#x27;t forget to set the Allowed- headers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;notes-about-logging-in&quot;&gt;Notes about logging in&lt;a class=&quot;zola-anchor&quot; href=&quot;#notes-about-logging-in&quot; aria-label=&quot;Anchor link for: notes-about-logging-in&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If your widget needs to allow people to log in, you will need to use a popup on your domain, since you won&#x27;t be able to set the cookies from an another domain, more on that later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-practice&quot;&gt;The practice&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-practice&quot; aria-label=&quot;Anchor link for: the-practice&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Most of the P4W widgets are doing the same thing: get data from Playfire, display them and modify it if you&#x27;re logged in (there are some read-only widgets though).
Creating some kind of frameworks thus made sense.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;choosing-the-tools&quot;&gt;Choosing the tools&lt;a class=&quot;zola-anchor&quot; href=&quot;#choosing-the-tools&quot; aria-label=&quot;Anchor link for: choosing-the-tools&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Since your javascript will be loaded on external websites, your impact on the page needs to be as small as possible so you can&#x27;t just add jQuery or any library you want.
After looking around, I settled on these libraries:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ded&#x2F;qwery&quot; title=&quot;qwery&quot;&gt;qwery&lt;&#x2F;a&gt; for the css selector&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ded&#x2F;bonzo&quot; title=&quot;bonzo&quot;&gt;bonzo&lt;&#x2F;a&gt; for the dom manipulation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fat&#x2F;bean&quot; title=&quot;bean&quot;&gt;bean&lt;&#x2F;a&gt; for the events&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;easyxdm.net&#x2F;wp&#x2F;&quot; title=&quot;easyXDM&quot;&gt;easyXDM&lt;&#x2F;a&gt; for the cross origin messaging&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;browserify.org&#x2F;&quot; title=&quot;browserify&quot;&gt;browserify&lt;&#x2F;a&gt; to create bundles and use require like in node.js&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;handlebarsjs.com&#x2F;&quot; title=&quot;handlebars&quot;&gt;Handlebars&lt;&#x2F;a&gt; for the templates&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These 6 libraries are the core of P4W.
If I started now, I might have chosen &lt;a href=&quot;http:&#x2F;&#x2F;minifiedjs.com&#x2F;&quot; title=&quot;Minified&quot;&gt;Minified&lt;&#x2F;a&gt; to replace qwery, bonzo and bean.&lt;&#x2F;p&gt;
&lt;p&gt;The build is using &lt;a href=&quot;http:&#x2F;&#x2F;gruntjs.com&#x2F;&quot; title=&quot;Grunt&quot;&gt;Grunt&lt;&#x2F;a&gt; and we use &lt;a href=&quot;http:&#x2F;&#x2F;sass-lang.com&#x2F;&quot; title=&quot;Sass&quot;&gt;Sass&lt;&#x2F;a&gt; for the style.
We currently only have integration tests using &lt;a href=&quot;http:&#x2F;&#x2F;casperjs.org&#x2F;&quot; title=&quot;CasperJS&quot;&gt;CasperJS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-a-framework&quot;&gt;Creating a framework&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-a-framework&quot; aria-label=&quot;Anchor link for: creating-a-framework&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Since all of those widgets have a similar pattern, creating some kind of framework makes sense.
It is organised the following way:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; style
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── css
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── sass
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; templates
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── widget1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── widget1.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; main.js
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; login.js
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; widget1.js
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All of these files will be linted and concatenated by the Grunt task to form the lib.js loaded by the embed.js mentionned in the snippet above.&lt;&#x2F;p&gt;
&lt;p&gt;The main.js is the entry point of the framework: it requires all the other javascripts files and use qwery to detect whether the page it&#x27;s loaded in contains
HTML elements having classes corresponding to widgets and will initialise them.
This will also detect whether one of those widgets needs to know if a user is logged in or not in Playfire (for example the blog comment will need to) and do that call to
Playfire while the widgets are initialising.
Widgets will then do calls for their own data and display themself.&lt;&#x2F;p&gt;
&lt;p&gt;If the user is not logged and an action like a click on the Want button requires the logged in state, a login popup will open, with its URL on the playfire domain so it can properly set the cookie as mentionned above.
The tricky part of that bit is actually closing the popup once the use logged in.
With most browsers, you can use postMessage to pass the data back to the opener page but this doesn&#x27;t work with IE.
The easy (not so good) solution is to poll the server for login status once the popup has been opened for a while and close it on success response or timeout.&lt;&#x2F;p&gt;
&lt;p&gt;The widgets themselves all have a similar structure, allowing for copy&#x2F;paste of it and then filling in the blank.
Using a common class for that would probably be better but some widgets (thinking of the comments one) can be quite different from others.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing&quot;&gt;Testing&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing&quot; aria-label=&quot;Anchor link for: testing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Testing third-party widgets is a bit of a pain.
Since most of the logic will be in the server and it can be unit tested properly, I mainly focused on testing integration on the javascript side.
This means tests are not that fast (around 5s for one) but if the tests pass, there&#x27;s no reason why the widgets wouldn&#x27;t work on live.
CasperJS was a very nice interface for PhantomJS and is quite fun to use.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;things-to-watch-out-for&quot;&gt;Things to watch out for&lt;a class=&quot;zola-anchor&quot; href=&quot;#things-to-watch-out-for&quot; aria-label=&quot;Anchor link for: things-to-watch-out-for&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;While this hasn&#x27;t happened to us yet since the widgets are mainly on pages we control, you have to be careful with the javascript&#x2F;css you&#x27;re using.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;javascript&quot;&gt;Javascript&lt;a class=&quot;zola-anchor&quot; href=&quot;#javascript&quot; aria-label=&quot;Anchor link for: javascript&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Remember, this file will be executed on a third-party website and you don&#x27;t control what they are doing with their javascript.
They could have redefined undefined or added functions to every prototypes for all you know so you can&#x27;t make any assumption about what&#x27;s present or not so you need to develop with that in mind.
You also need to make sure you&#x27;re not leaking global variables of adding to prototypes yourself, which would mess up their own javascript.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;css&quot;&gt;CSS&lt;a class=&quot;zola-anchor&quot; href=&quot;#css&quot; aria-label=&quot;Anchor link for: css&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Again, you don&#x27;t need what kind of crazy design they might have and you don&#x27;t want your css to be applied to the whole website either.
The solution for that is to use highly specific CSS selectors and use &lt;strong&gt;defensive css&lt;&#x2F;strong&gt;.
By defensive css I mean the css to ensure your widgets renders how it should. If your widget is a small box like the Tweet button, you can&#x27;t exactly have a text of font size 10em there.
Adding css rules to ensure minimal consistency is the key here. While you are not going to set the font type or color, you can definitely force the font-size for example.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While there&#x27;s not a lot of code to show since it&#x27;s not open source, I hope this article gave a good overview on how to approach the development of third party widget.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Starting an AngularJS project</title>
          <pubDate>Wed, 23 Oct 2013 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/starting-an-angularjs-project/</link>
          <guid>https://www.vincentprouillet.com/blog/starting-an-angularjs-project/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/starting-an-angularjs-project/">&lt;p&gt;I don&#x27;t think there is any need to introduce &lt;a href=&quot;http:&#x2F;&#x2F;angularjs.org&#x2F;&quot; title=&quot;AngularJS&quot;&gt;AngularJS&lt;&#x2F;a&gt; anymore, but here&#x27;s a (very) small introduction of it to make sure everyone knows the basics.&lt;&#x2F;p&gt;
&lt;p&gt;AngularJS is a client-side framework, replacing the jquery code usually present on most sites.
Instead of modifying the DOM on events manually (like displaying a div on a click event), AngularJS takes a declarative approach.&lt;&#x2F;p&gt;
&lt;p&gt;The following examples show the difference in paradigm between these two.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; jQuery version
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;my-button&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;click&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;my-div&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;show&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;lt;!-- AngularJS version --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;button ng-click=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;showDiv = !showDiv&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Click me&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;&#x2F;button&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;div ng-show=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;showDiv&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Hidden by default&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some people will not like the fact that you&#x27;re putting logic in the HTML, I personally much prefer this kind of approach to a
jquery based one, and I find it much easier to organise your code using directives, controllers, services than the usual jquery
spaghetti.&lt;&#x2F;p&gt;
&lt;p&gt;For a real overview, please check the official website.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;starting-a-new-project&quot;&gt;Starting a new project&lt;a class=&quot;zola-anchor&quot; href=&quot;#starting-a-new-project&quot; aria-label=&quot;Anchor link for: starting-a-new-project&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A problem new developers encounter is how to organise their code.
For simple (and I mean -learning how it works- kind of simple only), putting everything in one file is file but obviously does not work once you start working on bigger projects.
Since I like organising code properly, I looked around to see what was the recommended way for that in AngularJS project.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ng-boilerplate-by-josh-david-miller&quot;&gt;ng-boilerplate by Josh David Miller&lt;a class=&quot;zola-anchor&quot; href=&quot;#ng-boilerplate-by-josh-david-miller&quot; aria-label=&quot;Anchor link for: ng-boilerplate-by-josh-david-miller&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;While I didn&#x27;t find THE answer (there&#x27;s no imposed structure like in many server-side frameworks so everyone do their own thing, which can be great), I found ng-boilerplate by joshdmiller (link now dead).
This boilerplate separates code by feature, containing all the code (including css&#x2F;html) for that particular feature.
It also uses &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;angular-ui&#x2F;ui-router&quot; title=&quot;ui-router&quot;&gt;ui-router&lt;&#x2F;a&gt; which is quite awesome.
It also comes with a whole lot of grunt tasks to automate dev&#x2F;test&#x2F;release cycle: everytime you save a file, it runs the associated task with this type of file and reloads your page.
I used this boilerplate for a learning project, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;kCalculator&quot; title=&quot;kCalculator&quot;&gt;kCalculator&lt;&#x2F;a&gt; and while it is a very good start, I found it not that practical for my own use (I use Sass, it uses LESS for example) and it had lots of things that I didn&#x27;t really want.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-own-ng-boilerplate&quot;&gt;My own ng-boilerplate&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-own-ng-boilerplate&quot; aria-label=&quot;Anchor link for: my-own-ng-boilerplate&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Since I wanted to start a new project (which will be much more complex than kCalculator), I decided that this time I would use something tailored for me.
Rather than forking and changing most of the code of the original, I started from scratch and added feature by feature what I needed.
Result is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;ng-boilerplate&quot; title=&quot;ng-boilerplate&quot;&gt;ng-boilerplate&lt;&#x2F;a&gt; (yes the name is lazy).
The end results differs quite a bit from the original :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;no ng-min in the build (since you can do it by hand in your code)&lt;&#x2F;li&gt;
&lt;li&gt;coffeescript only&lt;&#x2F;li&gt;
&lt;li&gt;Sass with Foundation&lt;&#x2F;li&gt;
&lt;li&gt;templates and css in their own directory instead of inside their small &#x27;feature app&#x27;&lt;&#x2F;li&gt;
&lt;li&gt;reorganised grunt tasks&lt;&#x2F;li&gt;
&lt;li&gt;not concatenating dist libs with app libs (need to implement using CDN for dist libs on release task)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;architecture-example&quot;&gt;Architecture example&lt;a class=&quot;zola-anchor&quot; href=&quot;#architecture-example&quot; aria-label=&quot;Anchor link for: architecture-example&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here is the layout of the project I&#x27;m working on (this is still very early, probably going to change some things later on) :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; app
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── app.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── app.tests.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── home
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   └── home.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── teams
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   ├── list.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   ├── module.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   ├── service.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   └── tests
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │       └── list.tests.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── users
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── module.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── new.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── service.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; assets
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── img
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── fonts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; common
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── form
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── directives.coffee
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; index.html
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; style
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── app.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── _forms.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── _normalize.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── _settings.scss
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; templates
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; home
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── index.html
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; teams
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── list.html
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; users
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; main.html
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; new.html
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; verification.html
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All of the different &#x27;features&#x27; are nicely placed under the app folder and each file in it has a unique function.
The module.coffee under each looks something like the following:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;modules = [
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;#39;ui.router.state&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;#39;users.new&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;users = angular.module &amp;#39;arena.users&amp;#39;, modules
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;users.config [&amp;#39;$stateProvider&amp;#39;, ($stateProvider) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;  $stateProvider.state &amp;#39;users&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;    abstract: true
&lt;&#x2F;span&gt;&lt;span&gt;    url: &amp;#39;&#x2F;users&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    views:
&lt;&#x2F;span&gt;&lt;span&gt;      main:
&lt;&#x2F;span&gt;&lt;span&gt;        template: &amp;#39;&amp;lt;ui-view&#x2F;&amp;gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  $stateProvider.state &amp;#39;users.new&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;    url: &amp;#39;&#x2F;new&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    templateUrl: &amp;#39;users&#x2F;new.html&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    controller: &amp;#39;NewUserCtrl&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    data:
&lt;&#x2F;span&gt;&lt;span&gt;      pageTitle: &amp;#39;Register&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    $stateProvider.state &amp;#39;users.verification&amp;#39;,
&lt;&#x2F;span&gt;&lt;span&gt;      url: &amp;#39;&#x2F;verification&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      templateUrl: &amp;#39;users&#x2F;verification.html&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;      data:
&lt;&#x2F;span&gt;&lt;span&gt;        pageTitle: &amp;#39;Check your emails&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This file is the entry point of the feature, injecting all the different modules (list, create, etc) and defining the views (using ui-router).
All of the other files are standard AngularJS controllers, services, directives.
If I think something can be reused in another feature&#x2F;project, it goes in the common directory.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;So far I really like this organisation, and this is working well for now.
I will update it as the project grows bigger since I will probably find flaws by then.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-learn-angularjs&quot;&gt;How to learn AngularJS&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-learn-angularjs&quot; aria-label=&quot;Anchor link for: how-to-learn-angularjs&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;egghead.io&#x2F;&quot; title=&quot;egghead.io&quot;&gt;egghead.io&lt;&#x2F;a&gt;: short videos explaining very well everything&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.thinkster.io&#x2F;&quot; title=&quot;thinkster.io&quot;&gt;thinkster.io&lt;&#x2F;a&gt;: explains lots of concept using egghead videos as a base and expand on them&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Class-based views VS function-based views</title>
          <pubDate>Wed, 02 Oct 2013 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/using-class-based-views/</link>
          <guid>https://www.vincentprouillet.com/blog/using-class-based-views/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/using-class-based-views/">&lt;p&gt;The URL gives me away.
I do prefer class-based views (CBV) over function-based views (FBV) and will try to explain why in this article.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;presentation-of-cbv&quot;&gt;Presentation of CBV&lt;a class=&quot;zola-anchor&quot; href=&quot;#presentation-of-cbv&quot; aria-label=&quot;Anchor link for: presentation-of-cbv&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First thing, a quick introduction of CBV.
The &#x27;old&#x27; way of doings views in django is something like the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;myview&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;request.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;POST&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# do my POST action (form, saving)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# do my GET action
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An identical version using CBV would be:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MyView&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;View&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kwargs&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# do my get action
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;**&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kwargs&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# do my post action
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main advantage of using CBV lies in being able to inherit from mixins and making the code much more compact and readable (
this is obviously from my point of view, some people won&#x27;t like the fact that they don&#x27;t see exactly what&#x27;s happening).
For example, let&#x27;s say you want to process a form (very basic one, on GET returns the form, on POST check for errors and show them if
necessary or do something and redirect to another URL in case of success).&lt;&#x2F;p&gt;
&lt;p&gt;In a FBV, processing such a form looks like that:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;myview&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;request.method &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;POST&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;MyForm&lt;&#x2F;span&gt;&lt;span&gt;(request.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;POST&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;form.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;is_valid&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;(form)
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;HttpResponseRedirect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;reverse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;success&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;MyForm&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;render&lt;&#x2F;span&gt;&lt;span&gt;(request, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;template.html&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;form&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: form})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And in a CBV&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;MyView&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;FormView&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;  form_class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;MyForm
&lt;&#x2F;span&gt;&lt;span&gt;  success_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;reverse_lazy&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;success&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  template_name &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;template.html&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;form_valid&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;do_something&lt;&#x2F;span&gt;&lt;span&gt;(form)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;super&lt;&#x2F;span&gt;&lt;span&gt;(MyView, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;form_valid&lt;&#x2F;span&gt;&lt;span&gt;(form)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is so much neater imo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;go-cbv&quot;&gt;Go CBV !&lt;a class=&quot;zola-anchor&quot; href=&quot;#go-cbv&quot; aria-label=&quot;Anchor link for: go-cbv&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There are generic classes for every CRUD action and it&#x27;s easy to create your own mixins on top of it.
The most common would be a LoginRequired one, working like the login_required decorator.
A JsonResponse one is also a good example of a useful CBV mixin (for more, look at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;brack3t&#x2F;django-braces&quot; title=&quot;django-braces&quot;&gt;django-braces&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;One pattern that we use all the time in Hizard (and most web app need the same kind of features) is checking that the user is allowed to see&#x2F;do certains actions.
In this case, we have mixins like RestrictedDetailView, RestrictedListView, etc that does the check automatically for us, the only thing we have to do is inheriting from them.
How cool is that ?
Instead of having huge FBV unorganized (I&#x27;ve seen in the hundreds of line unfortunately), you can divide everything into methods neatly and have much cleaner and easier to read code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fbv-is-not-dead&quot;&gt;FBV is not dead !&lt;a class=&quot;zola-anchor&quot; href=&quot;#fbv-is-not-dead&quot; aria-label=&quot;Anchor link for: fbv-is-not-dead&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;FBV is the traditional way to represent requests handling in lots of frameworks&#x2F;languages, making it &lt;em&gt;much&lt;&#x2F;em&gt; easier for a new django dev to understand what&#x27;s going on.
On some cases, it even makes more sense to use them rather than FBV (I&#x27;m thinking about views with several forms for example).
You also need to take into account that most current codebase use FBV and lots of developers don&#x27;t know how&#x2F;don&#x27;t want to use CBV.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Seeing the number of people not liking CBV, I assume I&#x27;m in the minority of people that thinks they are a fantastic tool to make your code cleaner, reusable and easier to maintain (you want to add another check on your LoginRequired ? much more annoying to do with decorators and FBV).
Even if you&#x27;re not using generic CBV, just using a CBV inheriting from View will cleanly separate GET and POST.
The main problem with CBV is the documentation I think but once you get the hang of it, it&#x27;s awesome.
In the end it&#x27;s a matter of preference and what the team decides : I use CBV on my own projects and FBV at work.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Testing Django projects</title>
          <pubDate>Mon, 02 Sep 2013 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/testing-django-projects/</link>
          <guid>https://www.vincentprouillet.com/blog/testing-django-projects/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/testing-django-projects/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This post will introduce what I consider the best practices when testing a Django project.
As you&#x27;ve probably read 1000 times, testing is very important because without them, deploying
is pretty much a gamble on whether something is going to break or not.
There are a few different type of tests:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;unit tests: test a specific function, one path at a time (by path I mean if&#x2F;else conditions)&lt;&#x2F;li&gt;
&lt;li&gt;integration tests: test a whole user action, from the template to the model&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;What I do is unit tests all the functions I write (except the views) and integration tests for the views.
This ensures you got everything covered.&lt;&#x2F;p&gt;
&lt;p&gt;Whether you write the tests before the code (if you&#x27;re doing TDD) or after is up to you.
I like doing TDD for &#x27;easy&#x27; code when I know how I am going to do it but otherwise I first write a quick draft
that works, then code it again the TDD way.
I found that it results in better code using that process but again it&#x27;s up to you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;basics&quot;&gt;Basics&lt;a class=&quot;zola-anchor&quot; href=&quot;#basics&quot; aria-label=&quot;Anchor link for: basics&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;structure&quot;&gt;Structure&lt;a class=&quot;zola-anchor&quot; href=&quot;#structure&quot; aria-label=&quot;Anchor link for: structure&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I like putting all the tests in a top level package (ie at the same level as the apps) because I find it easier
to navigate.
Make sure you have one test file for models, views, forms, etc rather than putting it in a single file like the
default django does because it quickly becomes impossible to work with.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tools&quot;&gt;Tools&lt;a class=&quot;zola-anchor&quot; href=&quot;#tools&quot; aria-label=&quot;Anchor link for: tools&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Lots of packages exist to make testing easier.
Here are the ones I use :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;factoryboy.readthedocs.org&#x2F;en&#x2F;latest&#x2F;&quot; title=&quot;factory boy&quot;&gt;factory boy&lt;&#x2F;a&gt;: create objects easily (no more fixtures)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jezdez&#x2F;django-discover-runner&quot; title=&quot;django-discover-runner&quot;&gt;django-discover-runner&lt;&#x2F;a&gt;: enhancement of the django test runner (to allow putting tests wherever you want), it will become
the default test runner starting 1.6&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;mock.readthedocs.org&#x2F;en&#x2F;latest&#x2F;&quot; title=&quot;mock&quot;&gt;mock&lt;&#x2F;a&gt;: mocking library, for when you don&#x27;t really want to call some functions (usually external services), part of python standard library starting 3.3&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;django-webtest&quot; title=&quot;django-webtest&quot;&gt;django-webtest&lt;&#x2F;a&gt;: makes integration testing of views easier&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;nedbatchelder.com&#x2F;code&#x2F;coverage&#x2F;&quot; title=&quot;coverage&quot;&gt;coverage&lt;&#x2F;a&gt;: for when you want to know whether you&#x27;re getting close to 100% coverage or not&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I also like to modify the manage.py to ensure I don&#x27;t type to type the settings file to use, it looks like this :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;os
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;sys
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;__name__ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;__main__&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;sys.argv[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;test&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        os.environ.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;setdefault&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;DJANGO_SETTINGS_MODULE&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;myproject.settings.test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        os.environ.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;setdefault&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;DJANGO_SETTINGS_MODULE&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;myproject.settings.dev&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;django.core.management &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;execute_from_command_line
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;execute_from_command_line&lt;&#x2F;span&gt;&lt;span&gt;(sys.argv)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;tips-for-having-and-keeping-fast-tests&quot;&gt;Tips for having (and keeping) fast tests&lt;a class=&quot;zola-anchor&quot; href=&quot;#tips-for-having-and-keeping-fast-tests&quot; aria-label=&quot;Anchor link for: tips-for-having-and-keeping-fast-tests&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Nothing is worse than having a test suite slow you down when developing (especially if you&#x27;re doing TDD).
Here are a few of the settings I use to speed it up&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# IN-MEMORY TEST DATABASE
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# If you&amp;#39;re not using DB specific features
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;DATABASES &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;default&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;ENGINE&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;django.db.backends.sqlite3&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;NAME&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;:memory:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;USER&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;PASSWORD&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;HOST&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;PORT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# FAST HASHING FOR PASSWORDS
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;PASSWORD_HASHERS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;django.contrib.auth.hashers.UnsaltedMD5PasswordHasher&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Use syncdb instead of migrate if you&amp;#39;re using South
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;SOUTH_TESTS_MIGRATE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;False
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Also, be careful when testing with saving objects : only do it if it&#x27;s necessary (if you&#x27;re using factory_boy, it means using MyFactory.build() instead
MyFactory()).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing&quot; aria-label=&quot;Anchor link for: testing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;a-form-model&quot;&gt;A form&#x2F;model&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-form-model&quot; aria-label=&quot;Anchor link for: a-form-model&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I group these 2 as they are quite similar : basically if you&#x27;re writing a method on one of those (be it a custom clean() on a form or a method on a method
to deal with some business logic), it should have a test for each of the different &#x27;paths&#x27; it contains.
By path I mean every branch of code exists, which ideally range from 1 to 3 (more than that and it could mean that the method does too much).
Those are unit tests, testing one thing at a time so if a test fails, there should only one possible reason.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-view&quot;&gt;A view&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-view&quot; aria-label=&quot;Anchor link for: a-view&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;This is where webtest shines.
Most of the tests I write are actually integration tests, testing thoroughly every path of each view.
For example, for a FormView you will have 3 paths : first GET, success POST and POST with errors.
By testing all of the branches you reduce the likelihood of unexpected exceptions (oxymoron I know, exceptions are rarely expected).
Webtest allow you to easily login (no need for self.client.login(...) anymore), easily grab&#x2F;submit forms and access the DOM.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example of testing a landing page with an email field from a model called Interest :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;PublicViewsTests&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b06936;&quot;&gt;WebTest&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;test_anonymous_can_access_landing_page&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;reverse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;public:landing-page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(url)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(response.status_code, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;test_anonymous_can_add_email&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;reverse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;public:landing-page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(url).form
&lt;&#x2F;span&gt;&lt;span&gt;        form[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;email&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;easy@abc.com&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        form.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(Interest.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;test_invalid_interest_email_error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;reverse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;public:landing-page&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        form &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.app.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(url).form
&lt;&#x2F;span&gt;&lt;span&gt;        form[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;email&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;easyabc.com&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;        response &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;form.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;submit&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;follow&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(Interest.objects.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;(), &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# you could look in the dom for your error class or just use assertTemplateUsed instead
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assertContains&lt;&#x2F;span&gt;&lt;span&gt;(response, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;invalid&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If I wanted to be logged in, I would just need to pass my user object as a parameter to the get, ie self.app.get(url, user=self.user) for example.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mocking&quot;&gt;Mocking&lt;a class=&quot;zola-anchor&quot; href=&quot;#mocking&quot; aria-label=&quot;Anchor link for: mocking&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Sometimes you want to mock things it out. Things like call to a 3rd party service that can take time and require an internet connection.
You do not want to mock your own services though unless you have proper integration tests somewhere.
In the following example, I will mock a method from the Stripe API to prevent it from actually calling Stripe.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;stripe.Customer.retrieve&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    @patch.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;(stripe.Customer, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;update_subscription&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;test_subscribe_different_subscription&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;UpdateSubscriptionMock&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;CustomerRetrieveMock&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;        Changing to a different plan (or no previous plan) should call
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;        Stripe and save it locally
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        CustomerRetrieveMock.return_value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;StripeTest.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get_customer_response&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        UpdateSubscriptionMock.return_value &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;StripeTest.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;get_subscription_response&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;_subscribe_user&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;StripePlanFactory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;code&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;big&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.customer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;subscribe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;big&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assertTrue&lt;&#x2F;span&gt;&lt;span&gt;(CustomerRetrieveMock.called)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;assertTrue&lt;&#x2F;span&gt;&lt;span&gt;(UpdateSubscriptionMock.called)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Mocking 2 things here :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a class method (stripe.Customer.retrieve)&lt;&#x2F;li&gt;
&lt;li&gt;a method (update_subscription of stripe.Customer)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To accomplish that, you just need to add the right decorators (look into the doc for more info), add the mocks in the test parameters (order matters of course)
and then define a return_value to these mocks.
At the end of the tests I&#x27;m just making sure both methods were called and that&#x27;s it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;That&#x27;s it for how I test django apps, if you want more details or have better ideas, feel free to ask&#x2F;share !&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Provisioning and deploying this blog with Ansible</title>
          <pubDate>Sat, 17 Aug 2013 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.vincentprouillet.com/blog/deploying-this-blog-using-ansible/</link>
          <guid>https://www.vincentprouillet.com/blog/deploying-this-blog-using-ansible/</guid>
          <description xml:base="https://www.vincentprouillet.com/blog/deploying-this-blog-using-ansible/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Making changes by hand on a server is bad practice.
If you have several servers you might forget to modify all of them, will be time consuming and deploying will be tedious.
Or you get a new server and need to make it up and running quickly, are you going to remember everything you did on the current servers ?&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, lots of tools exist to automate the process : Chef, Puppet, Salt, Ansible and several others.
Because I don&#x27;t want to configure my server everytime and would like to automate the whole deploy thing, I chose to use
Ansible for it.
We&#x27;re also using Ansible for Hizard as well so there&#x27;s that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ansible-i-choose-you&quot;&gt;Ansible, I choose you!&lt;a class=&quot;zola-anchor&quot; href=&quot;#ansible-i-choose-you&quot; aria-label=&quot;Anchor link for: ansible-i-choose-you&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I use &lt;a href=&quot;https:&#x2F;&#x2F;www.ansible.com&#x2F;&quot; title=&quot;Ansible&quot;&gt;Ansible&lt;&#x2F;a&gt; because it&#x27;s the simplest solution I found.
Playbooks (Ansible terms for the file that contains the actions to do on a server) are in YAML and configuration files in Jinja2 templates.
You can&#x27;t get easier than that, no weird custom ruby stuff.
Also, it works by just SSHing to the servers, unlike the others that require to have a daemons on every servers.
Lastly, it&#x27;s written in Python, easy to write modules for and the community is very active.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installation-and-structure&quot;&gt;Installation and structure&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation-and-structure&quot; aria-label=&quot;Anchor link for: installation-and-structure&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As Ansible is Python, let&#x27;s just use a virtual env !&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; mkvirtualenv ansible
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; pip install ansible
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it really.&lt;&#x2F;p&gt;
&lt;p&gt;This is the structure I&#x27;m using :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; development
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; production
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;├──&lt;&#x2F;span&gt;&lt;span&gt; roles
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   ├── common
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │   └── tasks
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   │       └── main.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;   └── pelican
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── files
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   ├── key
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   ├── key.pub
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   └── known_hosts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── handlers
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   └── main.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── tasks
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   ├── deploy.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   └── main.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       ├── templates
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       │   └── nginx.conf.j2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;       └── vars
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;│&lt;&#x2F;span&gt;&lt;span&gt;           └── main.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;└──&lt;&#x2F;span&gt;&lt;span&gt; site.yml
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ll go over these files one by one.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;inventories-development-production&quot;&gt;Inventories (development, production)&lt;a class=&quot;zola-anchor&quot; href=&quot;#inventories-development-production&quot; aria-label=&quot;Anchor link for: inventories-development-production&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is where you define the hosts against which you are going to run your playbooks.
The development file contain only the VM I use to test the playbook and the production one includes the live server.
Inventory file are simple ini files (there are some specific features you can look at in Ansible doc) :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ini&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-ini &quot;&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;[blog]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;192.168.43.157
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(I&#x27;m using ip here but you can use domain name too of course).
In this file I listed the 192.168.43.157 ip as being part of the blog group.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playbooks-site-yml&quot;&gt;Playbooks (site.yml)&lt;a class=&quot;zola-anchor&quot; href=&quot;#playbooks-site-yml&quot; aria-label=&quot;Anchor link for: playbooks-site-yml&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is the main compononent of Ansible.
site.yml is the master playbook which does both the provisioning and the deploy, you can of course have a playbook specific to provision and one
specific to deploy if you prefer.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Update Ubuntu, install Pelican, grab the repo, generate the blog
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;hosts&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;blog
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;root
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;roles&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;common
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;pelican
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All these lines are worth explaining :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;name: describe what the playbook or task (details on tasks below) does&lt;&#x2F;li&gt;
&lt;li&gt;hosts: which group the playbook should be run against&lt;&#x2F;li&gt;
&lt;li&gt;user: which user you want to connect and run the playbook with (Ansible defaults to the user running the playbook, which is rarely what you want)&lt;&#x2F;li&gt;
&lt;li&gt;roles: this is new in Ansible 1.2. Roles are basically meant to divide a playbook in small subsets to make it more reusable and clear&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;roles-everything-in-roles-directory&quot;&gt;Roles (everything in roles&#x2F;directory)&lt;a class=&quot;zola-anchor&quot; href=&quot;#roles-everything-in-roles-directory&quot; aria-label=&quot;Anchor link for: roles-everything-in-roles-directory&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Roles are basically directories containing known directories: tasks, files, templates, handlers and vars.
All the main.yml in these folders will automatically be added to the play and you won&#x27;t need to specify path for files in files and templates.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tasks-roles-tasks-yml&quot;&gt;Tasks (roles&#x2F;*&#x2F;tasks&#x2F;*.yml)&lt;a class=&quot;zola-anchor&quot; href=&quot;#tasks-roles-tasks-yml&quot; aria-label=&quot;Anchor link for: tasks-roles-tasks-yml&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;These are the actions the play will execute
Here&#x27;s the roles&#x2F;pelican&#x2F;tasks&#x2F;main.yml :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Install required system packages.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;apt&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;pkg={{ item }} state=installed
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;with_items&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;{{ packages }}&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Create user
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;home={{ user_home }} name={{ user }} shell=&#x2F;bin&#x2F;bash state=present
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Add user to sudo group
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;name={{ user }} groups=sudo append=true
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Create the SSH directory.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;state=directory path={{ user_ssh_directory }}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Upload SSH known hosts.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;copy&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;src=known_hosts dest={{ user_ssh_directory }}known_hosts mode=0600
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Upload SSH private key.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;copy&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;src=key dest={{ user_ssh_directory }}id_rsa mode=0600
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Add authorized key for the user
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;authorized_key&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;user={{ user }} key=&amp;#39;{{ item }}&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;with_file&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;key.pub
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Change permissions
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;shell&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;chown -R {{ user }}:{{ user }} {{ user_home }}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Deploy is in a different file because it might get long
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;deploy.yml
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This uses several &lt;a href=&quot;https:&#x2F;&#x2F;docs.ansible.com&#x2F;ansible&#x2F;latest&#x2F;modules&#x2F;modules_by_category.html&quot; title=&quot;Ansible modules&quot;&gt;modules&lt;&#x2F;a&gt; but this is really how Ansible works : give a
name to a task and the command to execute, using a module or a raw command.
I include the deploy.yml instead of listing the tasks in main.yml because I might want to create another playbook only for deploy and it&#x27;s clearer
that way anyway.
All the {{ }} elements are variables explained in the next part.
Since you probably don&#x27;t want to run the whole play everytime you&#x27;re deploying something, you can tag some tasks and run tasks by tags later.
Here is the deploy.yml :&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;# Deploy part of the playbook
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Pull source from bitbucket
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;repo={{ repo }} dest={{ blog_directory }}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;yes
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo_user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;{{ user }}&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;deploy
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Init submodules
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;git submodule update --init --recursive chdir={{ blog_directory }}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;yes
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo_user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;{{ user }}&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;deploy
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Install requirements
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;requirements={{ blog_directory }}requirements.txt virtualenv={{ user_home }}env
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;yes
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo_user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;{{ user }}&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;deploy
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Generate blog
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;shell&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;. {{ user_home }}env&#x2F;bin&#x2F;activate &amp;amp;&amp;amp; make publish chdir={{ blog_directory }}site
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;yes
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;sudo_user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;#39;{{ user }}&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;deploy
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;Copy nginx config (as I might add some custom stuff for some urls)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;template&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;src=nginx.conf.j2 dest=&#x2F;etc&#x2F;nginx&#x2F;sites-enabled&#x2F;{{ user }}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;notify&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;reload nginx
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;tags&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;deploy
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Several things going on there.
First you can notice that we&#x27;re using sudo and sudo_user because we want to run these steps as the pelican user and not root (not very satistied
of that solution, would probably make more sense to have a deploy role using the pelican user directly).
All the tasks are using the deploy tag, meaning that I will be able to run only those tasks later if I choose to.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;variables-roles-vars-yml&quot;&gt;Variables (roles&#x2F;*&#x2F;vars&#x2F;*.yml)&lt;a class=&quot;zola-anchor&quot; href=&quot;#variables-roles-vars-yml&quot; aria-label=&quot;Anchor link for: variables-roles-vars-yml&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;These values will be added into the play and usable throughout the play.
In this case, I&#x27;m using the variables in tasks and templates (the nginx config).
You can also define variable specific to a group by putting them in a group_vars folder in a top directory and naming the yml file the name of the
group (or use all.yml if you want the variables to be available for every groupe).
You can use interpolation from within the file itself, but you need to use the ${} syntax of the {{ }} (if anyone knows how to make it work using {{ }} ).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;pelican
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;user_home&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&#x2F;home&#x2F;${user}&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;user_ssh_directory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;${user_home}.ssh&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;blog_directory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;${user_home}blog&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;serve_directory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;${blog_directory}site&#x2F;output&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;repo&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;git@bitbucket.org:Keats&#x2F;perso.git
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;packages&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;build-essential
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;python2.7-dev
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;git
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;fail2ban
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;python-virtualenv
&lt;&#x2F;span&gt;&lt;span&gt;  - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;nginx
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;handlers-roles-handlers-yml&quot;&gt;Handlers (roles&#x2F;*&#x2F;handlers&#x2F;*yml)&lt;a class=&quot;zola-anchor&quot; href=&quot;#handlers-roles-handlers-yml&quot; aria-label=&quot;Anchor link for: handlers-roles-handlers-yml&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Handlers are tasks that you reference by name (you can see the notify: reload nginx in the deploy.yml).
They are basically used to restart&#x2F;reload services.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;reload nginx
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;service&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;name=nginx state=reloaded
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;running-it&quot;&gt;Running it&lt;a class=&quot;zola-anchor&quot; href=&quot;#running-it&quot; aria-label=&quot;Anchor link for: running-it&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To run a playbook, just specify which inventory file you want to use with -i flag and the name of the playbook file.
If you&#x27;re logging in with a password, you will need the --ask-pass option.
You can also limit which tasks should be run using the tags parameter.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --ask-pass -i&lt;&#x2F;span&gt;&lt;span&gt; development site.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; ansible-playbook&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --ask-pass -i&lt;&#x2F;span&gt;&lt;span&gt; development site.yml&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt; --tags &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;deploy&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Having the whole process automated means I can get a new server and have it up and running in less than 5 minutes and that&#x27;s pretty cool.
I&#x27;m also going to put the deploy in the post-commit hook so I won&#x27;t even have to run the playbook manually.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
