<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Blavier</title>
    <description>The latest articles on DEV Community by Christian Blavier (@cblavier).</description>
    <link>https://dev.to/cblavier</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F344617%2F95843ef3-569b-4810-82dc-56c5281a09ba.jpeg</url>
      <title>DEV Community: Christian Blavier</title>
      <link>https://dev.to/cblavier</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cblavier"/>
    <language>en</language>
    <item>
      <title>On Elixir Metaprogramming</title>
      <dc:creator>Christian Blavier</dc:creator>
      <pubDate>Mon, 23 Nov 2020 17:00:00 +0000</pubDate>
      <link>https://dev.to/cblavier/on-elixir-metaprogramming-abd</link>
      <guid>https://dev.to/cblavier/on-elixir-metaprogramming-abd</guid>
      <description>&lt;p&gt;&lt;em&gt;Metaprogramming&lt;/em&gt; is a scary word: it sounds like voodoo for programmers, and in some manners it is. But it is used by most popular Elixir libraries (Ecto, Phoenix and Elixir itself) and as I leveled up as an Elixir programmer, I needed to understand how it worked under the hood.&lt;/p&gt;

&lt;p&gt;I personally felt intimidated by the topic, and I owned the &lt;a href="https://pragprog.com/titles/cmelixir/metaprogramming-elixir/"&gt;&lt;em&gt;Metaprogramming Elixir&lt;/em&gt; book&lt;/a&gt; for more than a year before actually reading it 😱&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dxuKZa6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.christianblavier.com/content/images/2020/11/tempImageemj1Me.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dxuKZa6q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.christianblavier.com/content/images/2020/11/tempImageemj1Me.gif" alt="On Elixir Metaprogramming"&gt;&lt;/a&gt;My Elixir personal library &lt;/p&gt;

&lt;p&gt;But for any seasoned Elixir programmer, it is an excellent handbook and very pleasant to read. It is also pragmatic and I concur 100% with Chris McCord’s introductory advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rule 1 : Don't Write Macros - Chris McCord&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Be cautious with &lt;em&gt;metaprogramming&lt;/em&gt; as it is hardly maintainable and a pain to debug; don't even think about writing your application core features with it. It is more relevant for technical libraries, custom DSL and any other specific subjects.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a kind of magic
&lt;/h2&gt;

&lt;p&gt;As many of you, I came from a Ruby on Rails development background and I remember that while learning Rails, I was so impressed by the language expressivity and its magic features. Coming then from a Java background, it was a blessing to me!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;irb&amp;gt; 2.years.from_now + 5.days.from_now 
#=&amp;gt; Mon, 28 Nov 2022 13:38:36 UTC

irb&amp;gt; Person.find_by_user_name_and_password(user_name, password)
#=&amp;gt; #&amp;lt;Person id: 10, user_name: "Ryan"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Ruby magic in action





&lt;p&gt;Rails made indeed an intensive usage of Ruby dynamic features to extend the language at runtime: the &lt;code&gt;find_by_user_name_and_password&lt;/code&gt; call  just above is an example of dynamically generated class method based on the &lt;code&gt;Person&lt;/code&gt; model attributes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GM4Apl3a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/90c9a1015850dff7dece6249e4a25eef.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GM4Apl3a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/90c9a1015850dff7dece6249e4a25eef.jpg" alt="On Elixir Metaprogramming"&gt;&lt;/a&gt;How I felt when learning Ruby on Rails&lt;/p&gt;

&lt;p&gt;But there are caveats to Ruby magic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/441717/whats-wrong-with-magic"&gt;too much magic&lt;/a&gt; may hurt as you lack the understanding of what really happens under the hood. Same logic also applies to Elixir &lt;em&gt;metaprogramming&lt;/em&gt; (remember Chris McCord's motto? "&lt;em&gt;Don't Write Macros"&lt;/em&gt; 😅)&lt;/li&gt;
&lt;li&gt;Ruby magic is &lt;strong&gt;dynamic&lt;/strong&gt; by nature which means it is performed at runtime, at the expense of performance. Whereas &lt;strong&gt;Elixir macros are evaluated at compile time&lt;/strong&gt; and inserted in your bytecode as if it were manually written code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is metaprogramming?
&lt;/h2&gt;

&lt;p&gt;Before going deeper in the subject, it’s worth reminding that &lt;em&gt;metaprogramming&lt;/em&gt; is the art of writing code that will generate code. &lt;em&gt;Metaprogramming&lt;/em&gt; features are first class members of the Elixir language which provides macro API to rapidly extend the core language.&lt;/p&gt;

&lt;p&gt;Did you know that &lt;em&gt;if-then-else&lt;/em&gt; or &lt;em&gt;unless&lt;/em&gt; block syntax are written in Elixir through its macro mechanism? It provides syntaxic sugar over a binary case pattern matching structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmacro macro_unless(clause, do: expression) do
  quote do
    if(!unquote(clause), do: unquote(expression))
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
unless block written with an Elixir macro





&lt;p&gt;You could also be using meta-programmed features everyday without knowing it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/elixir-lang/elixir/tree/master/lib/ex_unit"&gt;ExUnit&lt;/a&gt; test definitions leverages on macros&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elixir-ecto/ecto/blob/master/lib/ecto/query.ex"&gt;Ecto querying DSL&lt;/a&gt; can perfectly mimic SQL language thanks to &lt;em&gt;metaprogramming&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Of course Chris McCord’s Phoenix framework uses macro a lot, like the &lt;a href="https://hexdocs.pm/phoenix/routing.html"&gt;Routing DSL&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;By the way, did you know that more than 90% of the Elixir sourcecode is written in Elixir? Meta enough for you? 🤯&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Elixir Macros 101
&lt;/h2&gt;

&lt;p&gt;Your first Elixir macros will be a bit tedious to write. I won’t give a full course about Elixir &lt;em&gt;metaprogramming&lt;/em&gt; (buy &lt;a href="https://pragprog.com/titles/cmelixir/metaprogramming-elixir/"&gt;Chris McCord’s book&lt;/a&gt;!) but I will instead give you a few keys to understand it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RqnCpkiT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-21-at-08.29.12%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RqnCpkiT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-21-at-08.29.12%402x.png" alt="On Elixir Metaprogramming"&gt;&lt;/a&gt;Can't blame &lt;a href="https://twitter.com/matt_chabert"&gt;Matthieu&lt;/a&gt;, happened to me as well!&lt;/p&gt;

&lt;p&gt;The main thing you need to understand is that all code you write in Elixir is represented in memory as an &lt;em&gt;Abstract Syntax Tree&lt;/em&gt; (AST). The compiler will parse your code and turn it into an AST, then the BEAM will evaluate the AST in order to run it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eISjbSOu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/Untitled-2020-11-20-0920.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eISjbSOu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/Untitled-2020-11-20-0920.png" alt="On Elixir Metaprogramming"&gt;&lt;/a&gt;Abstract Syntax Tree (AST)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Metaprogramming&lt;/em&gt;, which we previously defined as « code to generate code » could be achieved by generating raw Elixir code (as strings) but it would be unefficient and hardly maintainable.&lt;/p&gt;

&lt;p&gt;That’s why Elixir provides API’s to generate and evaluate AST. Here comes &lt;code&gt;quote&lt;/code&gt;: an Elixir primitive which can convert any Elixir code into its AST.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; quote do 5 + 8 * 3 end
#=&amp;gt; {:+, [context: Elixir, import: Kernel],
#=&amp;gt; [5, {:*, 
#=&amp;gt; [context: Elixir, import: Kernel], 
#=&amp;gt; [8, 3]
#=&amp;gt; ]}]
#=&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code can be evaluated the same way you would have evaluated code in a string (think &lt;code&gt;eval&lt;/code&gt; in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"&gt;Javascript&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; Code.eval_string("5 + 8 * 3")
#=&amp;gt; {29, []}
iex(2)&amp;gt; Code.eval_quoted(quote do 5 + 8 * 3 end)
#=&amp;gt; {29, []}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now what happens if you try to evaluate code with dynamic parts?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; a = 5
iex(2)&amp;gt; Code.eval_string("3 * a")
#=&amp;gt; ** (CompileError) nofile:1: undefined function a/0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course &lt;code&gt;a&lt;/code&gt; is unknown to your evaluated code and a simple way to fix this would be to use text interpolation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; a = 5
iex(2)&amp;gt; Code.eval_string("3 * #{a}")
#=&amp;gt; {15, []}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well the good news is you can interpolate within your quoted code as well: use the &lt;code&gt;unquote&lt;/code&gt; primitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; a = 5
iex(2)&amp;gt; Code.eval_quoted(quote do 3 * unquote(a) end)
#=&amp;gt; {15, []}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple as that! So whenever you think you are lost between your &lt;code&gt;quote&lt;/code&gt; / &lt;code&gt;unquote&lt;/code&gt; blocks, just look at it as if it were text interpolation.&lt;/p&gt;

&lt;p&gt;Then you need to know about &lt;code&gt;defmacro&lt;/code&gt; which you probably already encountered in your Phoenix project. It's a special construct able to produce code, from quoted expressions and parameters.&lt;/p&gt;

&lt;p&gt;Here is an example that generates a few functions :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule MyMacro do
  defmacro generate_functions(name_and_values) do
    for {name, value} &amp;lt;- name_and_values do
      quote do
        def unquote(name)(), do: unquote(value)
      end
    end
  end
end

defmodule MyModule do
  require MyMacro

  MyMacro.generate_functions([{:one, 1}, {:two, 2}, {:three, 3}])
end

iex(1)&amp;gt; MyModule.two + MyModule.three 
#=&amp;gt; 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are quite a few other things to know when it comes to Elixir &lt;em&gt;metaprogramming&lt;/em&gt;, but &lt;code&gt;quote&lt;/code&gt;, &lt;code&gt;unquote&lt;/code&gt; and &lt;code&gt;defmacro&lt;/code&gt; are the basics and can already help you to build powerful stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, but what for?
&lt;/h2&gt;

&lt;p&gt;I will end this post with some examples of the way we use &lt;em&gt;metaprogramming&lt;/em&gt; in our Elixir projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extends Ecto query API
&lt;/h3&gt;

&lt;p&gt;Ecto provides a fully featured querying API that mimics SQL. It relies on Elixir Macros and will be able to trigger compilation errors whenever your query is malformed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex(1)&amp;gt; query = from u in User, where: u.age &amp;gt; 0, select: u.name
iex(2)&amp;gt; Repo.all(query)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Typical Ecto query





&lt;p&gt;But every now and then you'll want to use some specific database functions not available in Ecto API. You can then use &lt;code&gt;fragment&lt;/code&gt; Ecto function which let you insert raw SQL in your Ecto Query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query = 
  from u in User,
  where: is_nil(fragment("?-&amp;gt;&amp;gt;?", u.metadata, "phone"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Using fragment to access a field within a Postgres JSONB column





&lt;p&gt;But the proper way is to leverage on macros to extend the Ecto query API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule MyQueryMacros do
  defmacro jsonb_get(column, key) do
    quote do
      fragment("?-&amp;gt;&amp;gt;?", unquote(column), unquote(key))
    end
  end
 end

 query = 
   from u in User,
   where: is_nil(jsonb_get(u.metadata, "phone"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Same Ecto query using our homemade jsonb_get macro





&lt;h3&gt;
  
  
  Aliases &amp;amp; Imports
&lt;/h3&gt;

&lt;p&gt;As your Elixir codebase grows, you can see that you repeat over and over the same &lt;code&gt;alias&lt;/code&gt; and &lt;code&gt;import&lt;/code&gt; blocks at the head of your files. Not really &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;...&lt;/p&gt;

&lt;p&gt;Phoenix already offers a solution you can use through the &lt;code&gt;use&lt;/code&gt; / &lt;code&gt;__using__&lt;/code&gt; macro. To declare a &lt;em&gt;controller&lt;/em&gt; or a &lt;em&gt;view,&lt;/em&gt; phoenix suggests a bunch of default aliases and imports declared in &lt;code&gt;my_app_web.ex&lt;/code&gt; file. Here is an example of &lt;a href="https://changelog.com"&gt;changelog.com&lt;/a&gt; &lt;code&gt;*_web.ex&lt;/code&gt; file : &lt;a href="https://github.com/thechangelog/changelog.com/blob/master/lib/changelog_web.ex"&gt;changelog.com/changelog_web.ex file&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then use the macro with the following snippet, which will &lt;em&gt;alias&lt;/em&gt; and &lt;em&gt;import&lt;/em&gt; a few Phoenix modules and even generate some authorization related functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule MyAppWeb.MyController do
  use MyAppWeb, :controller

  # ...
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our own project, we also brewed our own &lt;em&gt;aliasing&lt;/em&gt; system that we can use like this to prevent us from declaring the same aliases over and over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule MyApp.Contracts.SomeContractService do
  use MyApp,
    aliases: [:campaigns, :contracts, :users],
    private_aliases: [:contracts]

  # ...
end   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I may write further about these specific macros in another post 😉&lt;/p&gt;

&lt;h3&gt;
  
  
  changix
&lt;/h3&gt;

&lt;p&gt;A final example of how we use macros in our main Elixir application is our &lt;strong&gt;application changelog&lt;/strong&gt;. We present new features, bug fixes and improvement to our users with a changelog sidebar (and a blinking badge to draw their attention).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7WLWHAA7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-23-at-17.16.53%402x-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7WLWHAA7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-23-at-17.16.53%402x-2.png" alt="On Elixir Metaprogramming"&gt;&lt;/a&gt;A changelog sidebar embedded in our webapp&lt;/p&gt;

&lt;p&gt;Instead of feeding this changelog from database, we use &lt;em&gt;markdown&lt;/em&gt; files with a &lt;a href="https://jekyllrb.com/docs/front-matter/"&gt;YAML front matter&lt;/a&gt; header.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E92JlU5y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-23-at-17.29.19%402x-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E92JlU5y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-23-at-17.29.19%402x-1.png" alt="On Elixir Metaprogramming"&gt;&lt;/a&gt;Our markdown changelog files, commited right along source code&lt;/p&gt;

&lt;p&gt;At first, we used to parse these files at runtime everytime a user triggered the sidebar, then we cached the HTML output in memory in order to improve performance.&lt;/p&gt;

&lt;p&gt;But after reading Chris McCord's book, we realized that these &lt;em&gt;markdown&lt;/em&gt; files could be considered as source code and be compiled as well (or at least processed at compile-time). Here comes &lt;a href="https://github.com/cblavier/changix"&gt;&lt;strong&gt;changix&lt;/strong&gt;&lt;/a&gt;: a library relying on macros to provide compile-time changelog features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cblavier/changix"&gt;cblavier/changix on Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;I hope that this post will help you feel more confortable with Elixir &lt;em&gt;metaprogramming&lt;/em&gt; and give you enough courage to hack yours; but remember Chris's advice and always be cautious 😅&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Boring is the new Black</title>
      <dc:creator>Christian Blavier</dc:creator>
      <pubDate>Mon, 02 Mar 2020 13:35:56 +0000</pubDate>
      <link>https://dev.to/cblavier/boring-is-the-new-black-434d</link>
      <guid>https://dev.to/cblavier/boring-is-the-new-black-434d</guid>
      <description>&lt;p&gt;The more I get experienced as a software engineer and a developer, the more I like to keep things simple. In this article I will explain why I believe that boring - yet pragmatic - choices are often the most powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did you say "&lt;strong&gt;boring"&lt;/strong&gt;?
&lt;/h2&gt;

&lt;p&gt;I borrowed this term from a talk given by two Doctolib software engineers (&lt;a href="https://www.doctolib.fr"&gt;Doctolib&lt;/a&gt; is a very successful french startup in the health space). In this &lt;a href="https://fr.slideshare.net/OCTOTechnology/la-duck-conf-the-boring-architecture"&gt;presentation&lt;/a&gt;, Nicolas &amp;amp; Michel explain how Doctolib is dealing with 30k+ simultaneous users with a very simple and straightforward Ruby on Rails architecture: mostly a monolithic web application and a SQL database.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O4INAZV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/CleanShot-2019-12-10-at-22.35.15%402x.png" class="article-body-image-wrapper"&gt;&lt;img alt src="https://res.cloudinary.com/practicaldev/image/fetch/s--O4INAZV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/CleanShot-2019-12-10-at-22.35.15%402x.png"&gt;&lt;/a&gt;Doctolib software architecture&lt;br&gt;
Before going deeper into software architecture, you may want to know that the &lt;em&gt;boring trend&lt;/em&gt; has also been mentioned in quite a few other fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LA Times journalist Christopher Hawthorne is praising boring &lt;strong&gt;building&lt;/strong&gt; architecture in his &lt;a href="https://www.latimes.com/entertainment/arts/la-ca-cm-building-type-boring-buildings-20171119-htmlstory.html"&gt;"Boring architecture? Yes please"&lt;/a&gt; paper.&lt;/li&gt;
&lt;li&gt;Nick Rollins helps us to remember that a &lt;a href="//Good%20UX%20=%20Boring%20UI.%20Don%E2%80%99t%20Be%20Creative"&gt;Good User Experience involves a Boring User Interface&lt;/a&gt; and urges not be creative when dealing with UI: don't mess with your users and just stick to the - &lt;em&gt;boring&lt;/em&gt; - established standards.&lt;/li&gt;
&lt;li&gt;And even Elon Musk named his company &lt;a href="https://www.boringcompany.com/"&gt;The Boring Company&lt;/a&gt; ;-) (terrible example, they are actually boring, ie. drilling, tunnels)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Boring architecture
&lt;/h2&gt;

&lt;p&gt;Let's focus again on software architecture: a few years ago I &lt;a href="https://www.christianblavier.com/rails-with-no-js-framework/"&gt;wrote an article&lt;/a&gt; about the choice of not using any JS Framework when libraries such as Angular or EmberJS were on the rise, but instead leveraging on a very classic MVC architecture with a thin JS layer. I since had excellent feedbacks on this approach even if I occasionally rely on ReactJS to handle very dynamic pieces of UI.&lt;/p&gt;

&lt;p&gt;Unfortunately it seems that my ideas are against the current flow, since average web applications kept getting more and more complex with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a lot of different &lt;strong&gt;storage solutions&lt;/strong&gt; (NoSQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL&lt;/strong&gt; replacing REST APIs&lt;/li&gt;
&lt;li&gt;The rise of &lt;strong&gt;micro-services architectures&lt;/strong&gt; (M/SOA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containerized&lt;/strong&gt; (Docker) and &lt;strong&gt;orchestrated deployments&lt;/strong&gt; (Kubernetes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These architectural solutions are impressive at helping  large scale companies to deal with their storage or operation issues. &lt;strong&gt;But what drives me crazy&lt;/strong&gt; is to see how many, seed or low-scale companies / softwares &lt;strong&gt;start using these technologies from the ground-up&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Micro Services architectures
&lt;/h3&gt;

&lt;p&gt;Let's take an example : &lt;strong&gt;Micro Services&lt;/strong&gt;. It looks so neat and clever on your PowerPoint slides, making you think you will &lt;em&gt;divide and conquer&lt;/em&gt; on your software by splitting the big business problems you are trying to solve in much smaller pieces of software - easier to manage.&lt;/p&gt;

&lt;p&gt;But M/SOA also induces a very important development overhead: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you want your micro-services to talk together, you'll have to tie them up with APIs (whereas a simple function call would have been much simpler and faster).&lt;/li&gt;
&lt;li&gt;You can't simply share code between your services. You'll have to build and release libraries shared between your micro-services, even for sharing a simple one-liner function across your services.&lt;/li&gt;
&lt;li&gt;Micro-services also mean multiple units of deployment, which is way more complicated to release and run than a single web application. People usually end up with solutions like Kubernetes.&lt;/li&gt;
&lt;li&gt;Versioning and release-management is really tough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This overhead will eventually drive you away from delivering actual business value, which is not only a terrible idea for early stage companies, but a handicap for any size of business.&lt;/p&gt;

&lt;h3&gt;
  
  
  Document databases
&lt;/h3&gt;

&lt;p&gt;Another example is the use of &lt;strong&gt;document oriented databases&lt;/strong&gt; (such as MongoDB or CouchDB). Such databases are designed with very attractive benefits compared to relational databases: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can store document (think &lt;em&gt;JSON object&lt;/em&gt;) instead of simples rows.&lt;/li&gt;
&lt;li&gt;database is schema-less meaning a single collection (ie. &lt;em&gt;table&lt;/em&gt;) can hold documents of different nature (new attributes ...) without any schema migration involved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, what these databases are not marketed for, is the feature they lack regarding SQL databases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you cannot join different collections together. Meaning that if you want to reach best performance, your must really think your schema upfront considering all your use cases and potential queries to store and structure your documents in an optimal way. But your business will change and so will your queries ... leading you eventually to either sub-optimal &lt;a href="https://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem-in-orm-object-relational-mapping"&gt;N+1 queries&lt;/a&gt; or de-normalized data structures which are a nightmare to maintain, making you build over-engineered &lt;a href="https://martinfowler.com/bliki/CQRS.html"&gt;CQRS&lt;/a&gt; architectures (overhead!)&lt;/li&gt;
&lt;li&gt;no &lt;a href="https://en.wikipedia.org/wiki/ACID"&gt;ACID transaction&lt;/a&gt;. You can live without it 90% of the time, but the remaining 10% are a real pain to work-around with software transaction and compensation mechanisms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4MI4vVz1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/02/nosql-small.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4MI4vVz1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2020/02/nosql-small.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used MongoDB for years and learned these lessons the hard way at the expense of days at tweaking for improved performance and using CQRS design pattern to maintain integrity between denormalized data collections.&lt;/p&gt;

&lt;p&gt;A few years later, when came the time to build a new product I adopted PostgreSQL and it was such a relief! Postgres even became stronger over years by including new features previously only available in some noSQL system (such as JSON column datatype, partitioning and sharding, or geographic search).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Majestic Monolith
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CndXIUwf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/1C0WR8CjuV3MCbuukjOqeUg.png" class="article-body-image-wrapper"&gt;&lt;img alt src="https://res.cloudinary.com/practicaldev/image/fetch/s--CndXIUwf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/1C0WR8CjuV3MCbuukjOqeUg.png"&gt;&lt;/a&gt;Monolith, by Rene Aigner&lt;br&gt;
Monolithic software architectures sound awkward, clumsy - of course &lt;em&gt;boring&lt;/em&gt; - and unable to face agile and fast changing requirements. But they are quite the opposite.&lt;/p&gt;

&lt;p&gt;Doctolib previous example has already proven me right, but so did &lt;a href="https://basecamp.com/"&gt;Basecamp&lt;/a&gt;. In &lt;a href="https://m.signalvnoise.com/the-majestic-monolith/"&gt;The Majestic Monolith&lt;/a&gt; post DHH explains how Basecamp is dealing with million of users with their large and monolithic application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run a small team, not a tech behemoth? Embrace the monolith and make it majestic. You Deserve It! - DHH, 2019&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Building your software as a monolith doesn't mean that you don't care about architecture and are willing to build a big plate of &lt;em&gt;spaghetti-ware&lt;/em&gt;. I sincerely believe that removing the clutter and the boilerplate of non essential technology will help you at focusing on things that really matter such as a well designed business layer and tons of unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can totally craft well-thought and battle-tested code in a - majestic - monolithic fashion&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Boring organizations
&lt;/h2&gt;

&lt;p&gt;Some might argue that such a simplistic architecture does not add up very well with the size of their team or organization. Please look at it the other way around: maybe that if you &lt;strong&gt;started building less complicated software, you might figure out that you need smaller teams?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;*"Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations"  - *Conway, 1967.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;50 years ago, John Conway stated about the deep link existing between the structure of software and the structure of organizations which build them. So keep with &lt;strong&gt;small teams&lt;/strong&gt;, hire &lt;strong&gt;jack-of-all-trades full-stack developers&lt;/strong&gt; and &lt;strong&gt;build clean, simple and pragmatic software&lt;/strong&gt;. I'm quite sure this advice should resonate with most companies building software ; not everyone is building Google or Facebook.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The patterns that make sense for organizations orders of magnitude larger than yours, are often the exact opposite ones that’ll make sense for you. It’s the essence of cargo culting. If I dance like these behemoths, surely I too will grow into one. I’m sorry, but that’s just not how the tango goes. - DHH, 2019&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;I don't know if building boring and simple softwares with smaller teams will become a new trend in upcoming years, but it is my motto since a few years and it gives strong results until now.&lt;/p&gt;

&lt;p&gt;You might wonder &lt;em&gt;"Will I get bored at building boring stuff?&lt;/em&gt; . I don't think you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building simple software does not mean at all being bored: you will deliver more value and joy to your users and it is among the most exciting things and what should fuel your days as a software developer.&lt;/li&gt;
&lt;li&gt;I'm still tech savvy and really interested in new technologies and frameworks, but I'm also getting older and wiser ;-) When considering a new technology I'm always thinking about the tradeoff between the provided value and the induced overhead.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>microservices</category>
      <category>monolith</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How we improved our Elixir build speed by 5x</title>
      <dc:creator>Christian Blavier</dc:creator>
      <pubDate>Tue, 08 Oct 2019 20:53:00 +0000</pubDate>
      <link>https://dev.to/cblavier/how-we-improved-our-elixir-build-speed-by-5x-3651</link>
      <guid>https://dev.to/cblavier/how-we-improved-our-elixir-build-speed-by-5x-3651</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ga83vUCs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/1__NyTuevjXFDbns3htQrN3g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ga83vUCs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/1__NyTuevjXFDbns3htQrN3g.jpeg" alt="How we improved our Elixir build speed by 5x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’m really happy and proud to say that since this story was first posted, I received great feedback and relay throughout the Elixir community! The last advice given in this article has indeed been taken as an inspiration by&lt;/em&gt; &lt;a href="https://medium.com/u/f31378845318?source=post_page-----d45393c6700f----------------------"&gt;&lt;em&gt;José Valim&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to add a&lt;/em&gt; &lt;a href="https://github.com/elixir-lang/elixir/pull/9422"&gt;&lt;em&gt;test partitioning feature&lt;/em&gt;&lt;/a&gt; &lt;em&gt;to Elixir&lt;/em&gt; 🤗&lt;/p&gt;




&lt;h1&gt;
  
  
  Some context
&lt;/h1&gt;

&lt;p&gt;I have been using Elixir + Phoenix on a daily basis for 3 years now, and almost since day one, my team and I have set up continuous integration (CI) and continuous delivery (CD) processes.&lt;/p&gt;

&lt;p&gt;Since we’re deploying our app on Heroku, we’ve been using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SemaphoreCI as our CI tool&lt;/strong&gt; (because &lt;a href="https://semaphoreci.com/"&gt;SemaphoreCI&lt;/a&gt; is available as a Heroku add-on, simple as that!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heroku native pipelines to perform CD&lt;/strong&gt; whenever CI is successful on our Git master branch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although our CD process has always been very reliable, our CI process gradually started to under-perform, becoming both slower and unreliable. Like boiling frogs, we did not really pay attention until very recently when &lt;strong&gt;we had to wait almost for 1 hour before being able to deploy our code to staging&lt;/strong&gt; : each CI run would take 15 minutes, and random test failures forced us to re-run each build a few times before getting the green light. This drove us really crazy, and led us to take action!&lt;/p&gt;

&lt;p&gt;In this article, we’ll talk about the actions we took in order to &lt;strong&gt;improve an unreliable&lt;/strong&gt; — randomly failing — &lt;strong&gt;15 minutes long CI process&lt;/strong&gt; to a &lt;strong&gt;solid and steady 3 minutes build.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  #1 — Improve worst tests
&lt;/h1&gt;

&lt;p&gt;First action is to find out the culprits: which tests are performing really slowly and which are failing randomly?&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;mix test --trace&lt;/code&gt; is very useful as it gives you detailed execution time, per test. But my favorite is &lt;code&gt;mix test --slowest 10&lt;/code&gt; which prints the list of the 10 worst performing tests. I have no general purpose advice to give about fixing these tests, but the slowest are usually quite easy to improve.&lt;/p&gt;

&lt;p&gt;Regarding randomly failing tests, two things can really help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a simple &lt;strong&gt;bash loop&lt;/strong&gt; to run a single test over and over. If it returns anything else than 0, the test is unreliable.
&amp;lt;!--kg-card-begin: markdown--&amp;gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(for i in {1..10}; do mix test test/lib/my_app/features/my_browser_test.exs:10 &amp;gt; /dev/null; echo $?; done) | paste -s -d+ - | bc

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;wait_until&lt;/code&gt; function that helps to fix unreliable browser tests (we're using &lt;a href="https://github.com/HashNuke/hound"&gt;Hound&lt;/a&gt; with &lt;em&gt;chromedriver&lt;/em&gt;). Here is the &lt;a href="https://gist.github.com/cblavier/1acccfd914e4cd6a51af0cf500dff6f8"&gt;gist&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A last ugly trick 🙈 I am ashamed of and I urge you not to use is to give another chance to your failing tests! Running your tests with &lt;code&gt;mix test || mix test --failed&lt;/code&gt; will automatically rerun any failed test and still give you a 0 exit code if it passes on the second time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NB4bY7y---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.christianblavier.com/content/images/2019/12/cat.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NB4bY7y---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://www.christianblavier.com/content/images/2019/12/cat.gif" alt="How we improved our Elixir build speed by 5x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pro tip: I strongly encourage you to read&lt;/em&gt; &lt;code&gt;mix help test&lt;/code&gt; &lt;em&gt;output: hidden gems in there!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  #2 — Cache all things
&lt;/h1&gt;

&lt;p&gt;At this stage, the build became way more reliable (99% success rate) but still really slow (~ 12 min).&lt;/p&gt;

&lt;p&gt;My first attempt to improve this was to see if the grass was greener at GitHub’s with their brand new &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt;, and if it would result in an improved build duration. Although promising, I did not manage to run any of our &lt;em&gt;chromedriver&lt;/em&gt; tests with their product, and execution time was anyway quite similar.&lt;/p&gt;

&lt;p&gt;I then decided to go back to Semaphore and migrate from the classic plan to their new &lt;a href="https://semaphoreci.com/"&gt;SemaphoreCI 2.0&lt;/a&gt;. The new Semaphore (like GitHub Actions &lt;em&gt;btw&lt;/em&gt;) requires you to describe your CI pipeline in a YAML file committed along your source code. In this YAML file you can run docker containers, arbitrary shell commands or specific semaphore commands, including the very useful &lt;a href="https://docs.semaphoreci.com/article/149-caching"&gt;cache commands&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;cache store&lt;/code&gt; and &lt;code&gt;cache restore&lt;/code&gt; at strategic locations you can make most of your builds way faster:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;by not re-downloading Elixir dependencies&lt;/li&gt;
&lt;li&gt;not re-compiling all Elixir code, only updated code&lt;/li&gt;
&lt;li&gt;not re-downloading NPM/Yarn dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Pro tip: have a look at fail_fast and auto_cancel Semaphore settings. Real time savers!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  #3 —Leverage on parallelism
&lt;/h1&gt;

&lt;p&gt;Caching really helped, and at this step we could have stopped improving the process as our build time already &lt;strong&gt;went down to 6 minutes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But reading SemaphoreCI documentation is insightful, and their concepts of blocks and jobs that can run either sequentially or in parallel suggest there is still room for improvement!&lt;/p&gt;

&lt;p&gt;Our build was really straight-forward then:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wYGtIgcL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/semaphore1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wYGtIgcL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/semaphore1.png" alt="How we improved our Elixir build speed by 5x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first improvement is to run in parallel what can be done in parallel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mix credo&lt;/code&gt; (the Elixir code analysis tool) and &lt;code&gt;mix test&lt;/code&gt; can be run at the same time. With a fail-fast strategy that stops running tests whenever &lt;em&gt;credo&lt;/em&gt; fails, it can be a real time saver!&lt;/li&gt;
&lt;li&gt;JS &amp;amp; CSS assets can be compiled at the same time than Elixir code: it can save up to one minute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final step is to split our tests in different batches to also run them in parallel. I wrote this &lt;a href="https://gist.github.com/cblavier/4e6fa2939145bfb56a02b2bd413fec60"&gt;custom test runner&lt;/a&gt; (which simply wraps &lt;code&gt;mix test&lt;/code&gt; shell command) that splits all test files in N batches (by hashing their filename) and only runs a single batch out of N:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Run all tests in 4 random batches
$&amp;gt; mix n_test 1 4
$&amp;gt; mix n_test 2 4
$&amp;gt; mix n_test 3 4
$&amp;gt; mix n_test 4 4

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can then leverage on SemaphoreCI &lt;a href="https://docs.semaphoreci.com/article/50-pipeline-yaml#parallelism"&gt;parallelism&lt;/a&gt; to run a single task N times with an incremental index passed as an environment variable.&lt;/p&gt;

&lt;p&gt;Our CI finally looks like this, running at a solid and steady 3mn pace!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fczF8124--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/semaphore2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fczF8124--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.christianblavier.com/content/images/2019/12/semaphore2.png" alt="How we improved our Elixir build speed by 5x"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pro tip: save time at learning Semaphore by reading our&lt;/em&gt; &lt;a href="https://gist.github.com/cblavier/db7fb947aa8158449608557faafb1216"&gt;&lt;em&gt;build pipeline&lt;/em&gt;&lt;/a&gt; &lt;em&gt;setup&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping up
&lt;/h1&gt;

&lt;p&gt;It has been known &lt;a href="https://martinfowler.com/articles/continuousIntegration.html"&gt;for years&lt;/a&gt; how important Continuous Integration is, for any size of software team. But it should be a source of trust, &lt;a href="https://rubyonrails.org/doctrine/#optimize-for-programmer-happiness"&gt;happiness&lt;/a&gt; and increased productivity, never a source of frustration!&lt;/p&gt;

&lt;p&gt;So start paying attention, and maybe some of the guidelines in this post will help you at improving your CI build!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB - I was not paid by SemaphoreCI to write this article, so feel free to experiment with any other CI software, as long as it offers you caching &amp;amp; parallelism.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>elixir</category>
      <category>ci</category>
    </item>
    <item>
      <title>Rails with no JS framework</title>
      <dc:creator>Christian Blavier</dc:creator>
      <pubDate>Fri, 28 Feb 2014 21:57:00 +0000</pubDate>
      <link>https://dev.to/cblavier/rails-with-no-js-framework-p8f</link>
      <guid>https://dev.to/cblavier/rails-with-no-js-framework-p8f</guid>
      <description>&lt;h3&gt;
  
  
  Because all apps don’t need Ember or Angular
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.christianblavier.com%2Fcontent%2Fimages%2F2019%2F12%2Flibrary.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.christianblavier.com%2Fcontent%2Fimages%2F2019%2F12%2Flibrary.jpeg" alt="Rails with no JS framework"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m writing code for over a decade now, consider myself as a geek, and as a geek I’m always very curious and excited about any new fancy technology released every now and then.&lt;/p&gt;

&lt;p&gt;These days client-side MVC frameworks are very trendy, and to be honest what I read and learned regarding &lt;em&gt;Ember&lt;/em&gt; or &lt;em&gt;Angular&lt;/em&gt; (among many others) looked really shiny and well-thought. These are typically frameworks that I would love to love.&lt;/p&gt;

&lt;p&gt;But right now, I don’t need them. &lt;strong&gt;Do you?&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The playground
&lt;/h3&gt;

&lt;p&gt;In addition to my freelancing activity, I’m running &lt;a href="http://www.folyo.me/" rel="noopener noreferrer"&gt;Folyo&lt;/a&gt; (which by the way, can help you to find great designer talents). It’s as &lt;em&gt;Ruby on Rails&lt;/em&gt; website with a UI that could be defined as slick, reactive and responsive (especially thanks to &lt;a href="http://sachagreif.com/" rel="noopener noreferrer"&gt;Sacha&lt;/a&gt;, my co-founder), but &lt;strong&gt;by no means complex&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a job board, Folyo has quite a common &lt;em&gt;page driven architecture&lt;/em&gt; where any action performed on a page will often lead you to another page or refresh current page content. Folyo’s UI &lt;strong&gt;does not involve rich interactions&lt;/strong&gt; like &lt;em&gt;live data charts&lt;/em&gt; or &lt;em&gt;dynamic data binding.&lt;/em&gt; No, just web pages and it’s perfectly fine!&lt;/p&gt;

&lt;h4&gt;
  
  
  So what about client-side frameworks?
&lt;/h4&gt;

&lt;p&gt;Why not, since it seems like the way to go these days?&lt;/p&gt;

&lt;p&gt;Because such frameworks are complex tools designed to solve complex interaction issues. You must be aware that they also have serious drawbacks regarding productivity, testing, SEO, etc. (read &lt;a href="https://sourcegraph.com/blog/switching-from-angularjs-to-server-side-html" rel="noopener noreferrer"&gt;this&lt;/a&gt;) and also have a steep learning curve.&lt;/p&gt;

&lt;p&gt;Building a website with client-side MVC frameworks will require you to build your Rails server-side application as an API application only, which means depriving you of some of Rails beauty and increasing your development effort (API + UI) significantly.&lt;/p&gt;

&lt;p&gt;Still, using a framework also brings some virtues. One of the first being to help you with organizing your code in a more manageable way. While Rails is really directive regarding the way you need to organize server-side code, it dictates only one rule for Javascript:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Put whatever you want in application.js.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eh. Really?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Time to clean it up
&lt;/h3&gt;

&lt;p&gt;I will now explain the way we decided to organize Javascript code on &lt;a href="http://www.folyo.me/" rel="noopener noreferrer"&gt;Folyo&lt;/a&gt;, in a much more manageable way than &lt;em&gt;everything in application.js&lt;/em&gt;. I believe it’s quite pragmatic and I’m sure it will fit with a lot of other applications.&lt;/p&gt;

&lt;p&gt;Required libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;jQuery&lt;/em&gt;, obviously.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Coffeescript&lt;/em&gt;, not mandatory but its syntax for class definition is very convenient.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Turbolinks.&lt;/em&gt; It will really improve user experience by making navigation from page to page AJAX only.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;HeadJS,&lt;/em&gt; used through &lt;em&gt;headjs-rails&lt;/em&gt; gem, will speed up your app by loading your JS asynchronously.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Code hierarchy
&lt;/h4&gt;

&lt;p&gt;To organize the code, I will simply obey to following rules:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AUpRTNIf-ll1ovWBUtolpJA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F800%2F1%2AUpRTNIf-ll1ovWBUtolpJA.png" alt="Rails with no JS framework"&gt;&lt;/a&gt;Folyo JS code hierarchy&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any page requiring JS code will have it’s own class (a JS view).&lt;/li&gt;
&lt;li&gt;If you need to share piece of code between views, put it in a widget class.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;application.js&lt;/em&gt; is just doing the glue between your classes, jQuery, HeadJS and Turbolinks.&lt;/li&gt;
&lt;li&gt;That’s all!&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Views
&lt;/h4&gt;

&lt;p&gt;ApplicationView is the default view, inherited by any other view, and instantiated by default when no specific view is provided.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.Views ||= {}
class Views.ApplicationView

 render: -&amp;gt;
   Widgets.FancyBox.enable()
   Widgets.MarkdownEditor.enable()

 cleanup: -&amp;gt;
   Widgets.FancyBox.cleanup()
   Widgets.MarkdownEditor.cleanup()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we want &lt;em&gt;FancyBox&lt;/em&gt; and our &lt;em&gt;Markdown&lt;/em&gt; editor to work on a lot of pages, we put it in the &lt;em&gt;ApplicationView&lt;/em&gt; as a default behavior.&lt;/p&gt;

&lt;p&gt;Then a typical view would look like that:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.Views.Newsletters ||= {}
class Views.Newsletters.EditView extends Views.ApplicationView

 render: -&amp;gt;
   super()
   $('a.preview').click (e) -&amp;gt;
     e.preventDefault()
     url = $(e.target).attr('href')
     window.open(url, '_blank', 'width=800,height=800')

 cleanup: -&amp;gt;
   super()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple as that! But why the cleanup stuff?&lt;/p&gt;

&lt;p&gt;Because with &lt;em&gt;Turbolinks,&lt;/em&gt; the Javascript environment is not reset between each page. For instance, if you define a timer on a page, it will keep ticking on next pages. So, just remember to stop your timer in the cleanup method (or remove any document-wide event listener)&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Widgets&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Sorry, no rocket science here.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.Widgets ||= {}

class Widgets.FancyBox
  @enable: -&amp;gt; $(".fancybox").fancybox()
  @cleanup: -&amp;gt; $(".fancybox").off()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The glue
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;application.js&lt;/em&gt; is now only the entry point which will listen to &lt;em&gt;Turbolinks&lt;/em&gt; events to render proper views and clean them up.#= require everything you need&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#= require everything you need

pageLoad = -&amp;gt;
  className = $('body').attr('data-class-name')
  window.applicationView = try
    eval("new #{className}()")
  catch error
    new Views.ApplicationView()
  window.applicationView.render()

head -&amp;gt;
  $ -&amp;gt;
    pageLoad()
    $(document).on 'page:load', pageLoad

    $(document).on 'page:before-change', -&amp;gt;
      window.applicationView.cleanup()
      true

    $(document).on 'page:restore', -&amp;gt;
      window.applicationView.cleanup()
      pageLoad()
      true

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to define a specific data attribute on each page, to indicate which JS view needs to be rendered.&lt;/p&gt;

&lt;p&gt;In your &lt;em&gt;application_controller.rb&lt;/em&gt;, define &lt;em&gt;js_class_name&lt;/em&gt; method:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def js_class_name
   action = case action_name
     when 'create' then 'New'
     when 'update' then 'Edit'
    else action_name
   end.camelize
   "Views.#{self.class.name.gsub('::', '.').gsub(/Controller$/, '')}.#{action}View"
 end

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then use it in your layout (as well as &lt;em&gt;HeadJS&lt;/em&gt; initialization)&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%html
  %head
    = javascript_include_tag 'head.min'
    = headjs_include_tag 'vendor', 'application'
  %body{'data-class-name' =&amp;gt; js_class_name}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final words
&lt;/h3&gt;

&lt;p&gt;I’m seing most and most Rails projects bootstrapped using either &lt;em&gt;Angular&lt;/em&gt;, &lt;em&gt;Ember&lt;/em&gt; or &lt;em&gt;Backbone&lt;/em&gt; as their &lt;em&gt;de facto&lt;/em&gt; choice.&lt;/p&gt;

&lt;p&gt;Whilst these frameworks are very powerful, I hope that after reading this post you will consider that &lt;em&gt;not using a JS framework&lt;/em&gt; is also a valid choice.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>ruby</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
