<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Bits of Code - Pieces of Writing]]></title><description><![CDATA[Christian Blavier, mostly about software engineering. Elixir / JS / Ruby]]></description><link>https://www.christianblavier.com/</link><image><url>https://www.christianblavier.com/favicon.png</url><title>Bits of Code - Pieces of Writing</title><link>https://www.christianblavier.com/</link></image><generator>Ghost 5.38</generator><lastBuildDate>Mon, 20 Apr 2026 00:05:49 GMT</lastBuildDate><atom:link href="https://www.christianblavier.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Boost your test coverage with Elixir]]></title><description><![CDATA[In this article, I will help you build the appropriate tooling to track and measure your test coverage, and hopefully improve it.]]></description><link>https://www.christianblavier.com/boost-your-test-coverage-with-elixir/</link><guid isPermaLink="false">629603e163e1200001252ac5</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Testing]]></category><category><![CDATA[CI]]></category><dc:creator><![CDATA[Christian Blavier]]></dc:creator><pubDate>Mon, 30 May 2022 08:53:43 GMT</pubDate><media:content url="https://www.christianblavier.com/content/images/2022/05/isaac-smith-6EnTPvPPL6I-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.christianblavier.com/content/images/2022/05/isaac-smith-6EnTPvPPL6I-unsplash.jpg" alt="Boost your test coverage with Elixir"><p>I&apos;m working for years with my team on a pretty large Elixir / Phoenix code base, and although we are all very sensitive to unit and automated testing, we have been a bit lax regarding test coverage.</p><p>Over the past year, we started refactoring a large part of our UI from React to Phoenix Liveview with a more thorough testing approach (because LiveView testing is &#x1F525;) and have been wondering: how much better is our test harness?</p><blockquote><strong>If you can&#x2019;t measure it, you can&#x2019;t improve it</strong> - Peter Drucker</blockquote><p>In this article, I will help you build the appropriate tooling to track and measure your test coverage, and hopefully improve it.</p><h2 id="iterative-feedback">Iterative feedback</h2><figure class="kg-card kg-image-card"><img src="https://www.christianblavier.com/content/images/2022/05/6eut9v.jpg" class="kg-image" alt="Boost your test coverage with Elixir" loading="lazy" width="800" height="450" srcset="https://www.christianblavier.com/content/images/size/w600/2022/05/6eut9v.jpg 600w, https://www.christianblavier.com/content/images/2022/05/6eut9v.jpg 800w" sizes="(min-width: 720px) 720px"></figure><h3 id="bot-chat">Bot chat</h3><p>As new code is produced and shipped, it&#x2019;s very legit to question to wonder <em>&#xAB; is this code tested well enough to be merged? &#xBB;</em>.</p><p>The best place to have this kind of conversation between a contributor and a peer reviewer is a <strong>pull request</strong> (we use GitHub&#x2019;s), so we will leverage our existing CI process to let us know how well our new feature is tested.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2022/05/CleanShot-2022-05-03-at-18.38.09@2x.png" class="kg-image" alt="Boost your test coverage with Elixir" loading="lazy" width="1842" height="355" srcset="https://www.christianblavier.com/content/images/size/w600/2022/05/CleanShot-2022-05-03-at-18.38.09@2x.png 600w, https://www.christianblavier.com/content/images/size/w1000/2022/05/CleanShot-2022-05-03-at-18.38.09@2x.png 1000w, https://www.christianblavier.com/content/images/size/w1600/2022/05/CleanShot-2022-05-03-at-18.38.09@2x.png 1600w, https://www.christianblavier.com/content/images/2022/05/CleanShot-2022-05-03-at-18.38.09@2x.png 1842w" sizes="(min-width: 720px) 720px"><figcaption>Automated pull request comments</figcaption></figure><p>The screenshot above illustrates what we aim at:</p><ul><li>a PR comment, easy to scan &amp; read &#x1F4AC;</li><li>a short feedback loop (one should not have to wait for hours) &#xA0;&#x26A1;&#xFE0F;</li><li>fully automated (posted by your CI) &#x1F916;</li></ul><h3 id="tweak-the-ci">Tweak the CI</h3><p>The first and foremost thing to do is to compute test coverage, locally. As Elixir developers, we are gifted with a wonderful library: <a href="https://github.com/parroty/excoveralls?ref=bits-of-code-pieces-of-writing">ExCoveralls</a>. It&apos;s quite simple to use (locally at least), and I will not give a thorough setup guide as the library itself is well documented.</p><p>When the library is set up you can run the following commands:</p><ul><li><code>mix coveralls.html</code> to get a formatted HTML page displaying coverage information</li><li><code>mix coveralls.json</code> to get the same data as JSON which could be interesting for automation (beware, there is a plot twist &#x1F440; )</li></ul><p>Now, let&apos;s have a look at our CI. We have been using <a href="https://semaphoreci.com/?ref=bits-of-code-pieces-of-writing">Semaphore CI</a> for years and are very satisfied with it (this article is not sponsored whatsoever). But I&apos;m certain you can use the CI tool of your choice, provided you have access to partition &amp; caching features.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2022/05/CleanShot-2022-05-03-at-19.07.39@2x-2.png" class="kg-image" alt="Boost your test coverage with Elixir" loading="lazy" width="800" height="828" srcset="https://www.christianblavier.com/content/images/size/w600/2022/05/CleanShot-2022-05-03-at-19.07.39@2x-2.png 600w, https://www.christianblavier.com/content/images/2022/05/CleanShot-2022-05-03-at-19.07.39@2x-2.png 800w" sizes="(min-width: 720px) 720px"><figcaption>Our CI pipeline</figcaption></figure><p>The screenshot above is our Semaphore pipeline. The important thing to notice is that our tests have been partitioned to get faster build feedback (please look at <a href="https://www.christianblavier.com/how-we-improved-our-elixir-build-speed-by-5x/">this post</a> if you want more details about test partitioning).</p><p>Partitioning is very structuring in the way we compute code coverage: we cannot use <code>mix coveralls.json</code> as-is in a new CI task as it would re-run all tests in a lengthy monolithic process, defeating the purpose of test partitions.</p><p>What we need is a way to run test coverage, partition-wise, and then collect &amp; merge all data (map-reduce style). To do so, we will switch to another coverage output format that can be easily merged afterward: <a href="https://wiki.documentfoundation.org/Development/Lcov?ref=bits-of-code-pieces-of-writing">LCOV</a>.</p><p>First, we change the <code>mix test</code> call to <code>mix coveralls.lcov</code> that will both run tests and compute coverage, with partitions and umbrella app support. Test coverage output (the <code>cover/lcov.info</code> file) is stored in <a href="https://docs.semaphoreci.com/essentials/caching-dependencies-and-directories/?ref=bits-of-code-pieces-of-writing">SemaphoreCI cache</a> so that data can be retrieved in a subsequent job.</p><pre><code class="language-bash">MIX_ENV=test MIX_TEST_PARTITION=$SEMAPHORE_JOB_INDEX mix coveralls.lcov --parallel --umbrella --partitions $SEMAPHORE_JOB_COUNT`
mv cover/lcov.info cover/lcov-$SEMAPHORE_JOB_INDEX.info
cache store coverage-$SEMAPHORE_WORKFLOW_ID-$SEMAPHORE_JOB_INDEX cover
</code></pre><p>Then we use an <code>after_pipeline</code> semaphore task to collect all coverage files and merge them using the <code>lcov</code> binary.</p><pre><code class="language-bash">for i in {1..8}; do cache restore coverage-$SEMAPHORE_WORKFLOW_ID-$i; done
find ./cover -name &quot;lcov-*.info&quot; -exec echo -a {} \; | xargs lcov -o cover/lcov.info
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2022/05/CleanShot-2022-05-03-at-20.29.47@2x-2.png" class="kg-image" alt="Boost your test coverage with Elixir" loading="lazy" width="894" height="587" srcset="https://www.christianblavier.com/content/images/size/w600/2022/05/CleanShot-2022-05-03-at-20.29.47@2x-2.png 600w, https://www.christianblavier.com/content/images/2022/05/CleanShot-2022-05-03-at-20.29.47@2x-2.png 894w" sizes="(min-width: 720px) 720px"><figcaption>The after pipeline steps</figcaption></figure><p>At this moment, you have a single <code>lcov.info</code> file that contains data for the whole project:</p><pre><code class="language-bash">~/code/elixir-project &#x21D2; lcov --summary cover/lcov.info
Reading tracefile coverage/lcov.info
Summary coverage rate:
  lines......: 59.6% (10440 of 17503 lines)
  functions..: no data found
  branches...: no data found
</code></pre><p>The next step is to post coverage data to GitHub. For that purpose, we&apos;re using the following <a href="https://gist.github.com/cblavier/9d54fae716b767599c4c3c11b6f68363?ref=bits-of-code-pieces-of-writing">shell script</a>; I won&apos;t delve into tedious shell intricacies, but here is what you need to know about this script:</p><ul><li>we use <a href="https://cli.github.com/?ref=bits-of-code-pieces-of-writing">gh CLI</a> to check if a pull request has been opened for the current branch</li><li>we parse the <code>lcov.info</code> output using <code>lcov --summary</code>, <code>grep</code> and <code>cut</code></li><li>we use the <a href="https://docs.semaphoreci.com/essentials/artifacts/?ref=bits-of-code-pieces-of-writing">semaphore CI artifact API</a> to store <code>lcov.info</code> for each branch. This way we can compare current branch coverage to master&apos;s (we do some basic maths with <code>calc</code> binary)</li><li>we use <code>gh</code> once again to post the pull request comment, and store the comment URL into semaphore cache. This way later builds will be able to update a single PR comment instead of posting a new one each time.</li></ul><p>You now have all the elements to get fast and iterative feedback on your application test coverage!</p><h2 id="improve-your-coverage">Improve your coverage</h2><p><strong>Pull request comments are great to get instant feedback</strong> regarding the global trend of test coverage, but <strong>it doesn&apos;t provide any detailed insight whatsoever</strong>. If you want to improve your code coverage, you need to get in-depth information about what code is already tested and what code has insufficient coverage.</p><p>For this purpose, we chose to rely on our main code editor: VSCode. It has a great plugin ecosystem and searching on the marketplace for <code>lcov</code> allowed us to find two great extensions that fit our needs.</p><ul><li><a href="https://marketplace.visualstudio.com/items?itemName=tenninebt.vscode-koverage&amp;ref=bits-of-code-pieces-of-writing">Koverage</a> to visualize code coverage of your project as a treeview</li><li><a href="https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters&amp;ref=bits-of-code-pieces-of-writing">Coverage Gutters</a> to visualize line-per-line test coverage of any single file</li></ul><p>Since coverage files are quite long to generate (on our project, we need about 15 to 20mn to run our full test suite) we will leverage our CI which is already computing <code>lcov</code> files for every branch. We just need to add the following line to our semaphore pipeline to make the <code>lcov</code> file available as a project artifact:</p><pre><code class="language-bash">artifact push project cover/lcov.info --destination coverage/$SEMAPHORE_GIT_BRANCH/lcov.info --force
</code></pre><p>Then the following shell script can be used to download <code>lcov</code> from CI for the current working branch (given CI has already been run once).</p><pre><code class="language-bash">project_id=&quot;redacted&quot;
current_branch=$(git rev-parse --abbrev-ref HEAD)
coverage_dir=&quot;coverage&quot;
local_path=&quot;$coverage_dir/lcov.info&quot;

if [ -n &quot;$SEMAPHORE_TOKEN&quot; ]; then
  url_encoded_path=$(jq -R -r @uri &lt;&lt;&lt; &quot;coverage/$current_branch/lcov.info&quot;)
  url=&quot;https://org.semaphoreci.com/projects/$project_id/artifacts/$url_encoded_path&quot;
  
  mkdir -p $coverage_dir
  curl -s -f -L -H &quot;Authorization: Token $SEMAPHORE_TOKEN&quot; $url --output $local_path

  if [ $? -eq 0 ]; then
    echo &quot;test coverage has been downloaded at local path $local_path&quot;
  else
    echo &quot;could not download test coverage from $url&quot;
  fi
else
  echo &quot;please set SEMAPHORE_TOKEN in .env&quot;
  echo &quot;grab your token there: https://me.semaphoreci.com/account&quot;
fi
</code></pre><p>We then run this script with a custom mix alias: <code>mix coverage</code>: &#xA0;here is a video of what everything looks like.</p><figure class="kg-card kg-video-card kg-card-hascaption"><div class="kg-video-container"><video src="https://www.christianblavier.com/content/media/2022/05/vscode-coverage.mp4" poster="https://img.spacergif.org/v1/3296x2028/0a/spacer.png" width="3296" height="2028" playsinline preload="metadata" style="background: transparent url(&apos;https://www.christianblavier.com/content/images/2022/05/media-thumbnail-ember196.jpg&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div><figcaption>Exploring test coverage with VSCode</figcaption></figure><h1 id="final-words">Final words</h1><p>I hope this article will help you track and improve your software testing strategy. I&apos;m sure this approach can still be improved and if you have any ideas, feel free to drop a comment below.</p>]]></content:encoded></item><item><title><![CDATA[Advent Of Code makes me a better programmer]]></title><description><![CDATA[Advent Of Code is a yearly event offered by Eric Wastl: every day before Christmas, you can unlock a daily code challenge and crack it with the programming language of your choice. I n this post I will share with you my Advent Of code takeaways.]]></description><link>https://www.christianblavier.com/advent-of-code-makes-me-a-better-programmer/</link><guid isPermaLink="false">629603e163e1200001252ac4</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Christian Blavier]]></dc:creator><pubDate>Mon, 11 Jan 2021 20:43:51 GMT</pubDate><media:content url="https://www.christianblavier.com/content/images/2021/01/adventofcode.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.christianblavier.com/content/images/2021/01/adventofcode.png" alt="Advent Of Code makes me a better programmer"><p><a href="https://adventofcode.com/?ref=bits-of-code-pieces-of-writing">Advent Of Code</a> is a yearly event offered by <a href="https://twitter.com/ericwastl?ref=bits-of-code-pieces-of-writing">Eric Wastl</a>: every day before Christmas, you can unlock <strong>a<strong> daily code challenge</strong></strong> and crack it with the programming language of your choice.</p><p>It&apos;s super funny, with a pleasant Xmas/elve/reindeer... background and a soft difficulty curve (anyone with some programming experience can take the first challenges).</p><p>This year was my second participation. For the first time I was able to go through all the 25 challenges (it took me a bit more than 25 days) and the only language I used was Elixir.</p><p>I will now share with you my <strong><strong>Advent Of code takeaways</strong></strong>.</p><h2 id="1-you-re-not-an-impostor-"><strong>1 - You&apos;re not an impostor &#x1F978;</strong></h2><p>Advent Of code is for anyone willing to improve their programming skills.</p><p>It doesn&apos;t matter if you are:</p><ul><li>a competitive <a href="https://en.wikipedia.org/wiki/Code_golf?ref=bits-of-code-pieces-of-writing">golf programmer</a> used to crack challenges in <a href="https://adventofcode.com/2020/leaderboard/day/25?ref=bits-of-code-pieces-of-writing">less than 5 minutes</a> to rank among the first &#x1F92F;</li><li>a student or just some folk learning programming</li><li>a seasoned programmer willing to crank up his skills ( &#x1F448; I&apos;m here )</li><li>a seasoned programmer learning a new language (I might do next season&apos;s AOC in Rust)</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2021/01/jim-carrey-2.gif" class="kg-image" alt="Advent Of Code makes me a better programmer" loading="lazy" width="320" height="180"><figcaption>Cranking up my typing skills</figcaption></figure><p>What relieved me the most was how welcoming and helpful the AOC community is. A new <a href="https://www.reddit.com/r/adventofcode/wiki/solution_megathreads?ref=bits-of-code-pieces-of-writing">Reddit thread</a> is opened every day and people are encouraged to share their solutions or ask for help.</p><h2 id="2-cs101-again-"><strong>2 - CS101 ... again &#x1F468;&#x200D;&#x1F393;</strong></h2><p>If like me you graduated computer science quite a while ago, AOC might be the perfect occasion to refresh your academic knowledge.</p><p>Advent of Code will make you think about:</p><ul><li><strong>A<strong>lgorithmic complexity</strong></strong>, also known as <em><em>&quot;</em>WTF<em> </em>why is <em>my code running for 30</em> <em>minutes and not yielding any result ?!?&quot;</em></em></li><li><strong>D<strong>ata structures</strong>.</strong> Some challenges are tough without proper data modeling. So I suggest you know the ins and outs of basic data structures (chained lists, trees, and graphs) and related algorithms (depth-first vs breadth-first tree traversal)</li><li><strong><a href="https://en.wikipedia.org/wiki/Formal_grammar?ref=bits-of-code-pieces-of-writing">Formal Grammar</a></strong> will help you with some challenge</li><li><strong><a href="https://en.wikipedia.org/wiki/Dynamic_programming?ref=bits-of-code-pieces-of-writing">D<strong>ynamic programming</strong></a></strong> also saved me an evening of hacking.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2021/01/infinite-loop.jpeg" class="kg-image" alt="Advent Of Code makes me a better programmer" loading="lazy" width="1280" height="720" srcset="https://www.christianblavier.com/content/images/size/w600/2021/01/infinite-loop.jpeg 600w, https://www.christianblavier.com/content/images/size/w1000/2021/01/infinite-loop.jpeg 1000w, https://www.christianblavier.com/content/images/2021/01/infinite-loop.jpeg 1280w" sizes="(min-width: 720px) 720px"><figcaption>Courtesy of <a href="https://twitter.com/GaryJGrady?ref=bits-of-code-pieces-of-writing">Grady</a></figcaption></figure><h2 id="3-coding-kata-"><strong>3 - Coding Kata &#x1F94B;</strong></h2><p>We are no machines and we are not required to know anything by heart: so much knowledge is reachable online, within a few seconds.</p><p>Still, knowing a few will help.</p><p>AOC helped me to muscle my memory, and I&apos;m now able to code most challenges in a single streak without reaching the documentation or copy-pasting from previous code. During this event, I reinforced my knowledge of some of the most important Elixir APIs (<code>Enum</code>, <code>Stream</code>, <code>String</code>, <code>Regex</code>).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://media.giphy.com/media/eiLJO8oDuWtU1xd5oc/giphy.gif" class="kg-image" alt="Advent Of Code makes me a better programmer" loading="lazy"><figcaption>Code Editor is your Dojo</figcaption></figure><p>By not abusing <em><em>alt-tab</em></em> and staying focused in your code editor you&apos;ll be able to reach what most athletes know as <strong><strong>flow state</strong></strong>: this mental state in which you feel the most enjoyed and productive.</p><p>Like athletes, everyday practice will give you reflex, confidence, and improve your judgment.</p><h2 id="4-and-a-few-elixir-lessons-"><strong>4 - And a few Elixir lessons &#x1F9EA;</strong></h2><p>Reddit hosts the biggest AOC community, but you can also look after smaller AOC sub-communities. I learned much from ad-hoc threads created on <a href="https://elixirforum.com/tag/advent-of-code?ref=bits-of-code-pieces-of-writing">Elixir Forum</a>.</p><p>Here are a few things I learned, mostly from reading at other&apos;s solutions:</p><ul><li><strong><a href="https://elixirforum.com/tag/advent-of-code?ref=bits-of-code-pieces-of-writing">Elixir comprehensions</a> are powerful</strong>. They let you iterate, combine, filter and reduce in few line of codes (I recommend you <a href="https://blog.lelonek.me/best-practices-of-comprehensions-in-elixir-310ae3167bd2?ref=bits-of-code-pieces-of-writing">this article</a>)</li><li><strong><strong>Prepending is not appending</strong> - <strong>Elixir lists are not arrays</strong></strong>. Those assumptions sound trivial but helped me make a 60 seconds program run within a few milliseconds. Elixir is immutable and as such if you want to append a single item to a list, Elixir will create a whole new copy of the list (Elixir can&apos;t modify the last item to point at your new item - immutable we said ;-). On the opposite, prepending to a list will only create a new item that will point to the previous untouched list. That&apos;s why Elixir gives us a powerful prepend operator <code>prepended_list = [item | list]</code></li><li><strong>Elixir most common APIs hide a few jewels</strong> that I never used in 4 years of Elixir Programming. With AOC I discovered <code><a href="https://hexdocs.pm/elixir/Enum.html?ref=bits-of-code-pieces-of-writing#chunk_every/4">Enum.chunk_every</a></code>, <code><a href="https://hexdocs.pm/elixir/Enum.html?ref=bits-of-code-pieces-of-writing#chunk_while/4">Enum.chunk_while</a></code>, <code><a href="https://hexdocs.pm/elixir/Enum.html?ref=bits-of-code-pieces-of-writing#zip/1">Enum.zip</a></code> or <code><a href="https://hexdocs.pm/elixir/Stream.html?ref=bits-of-code-pieces-of-writing#unfold/2">Stream.unfold</a></code></li><li>You have all <strong><strong>Erlang magic at hand</strong></strong>. I solved some of the challenges by using raw Erlang APIs such as <code><a href="https://erlang.org/doc/man/ets.html?ref=bits-of-code-pieces-of-writing">ets</a></code>, <code><a href="https://erlang.org/doc/man/digraph.html?ref=bits-of-code-pieces-of-writing">digraph</a></code>, or the fairly new <code><a href="https://erlang.org/doc/man/atomics.html?ref=bits-of-code-pieces-of-writing">atomics</a></code> module.</li><li><strong>Immutability brings safety</strong>. Some challenges were tricky to code in some other languages because if you didn&apos;t take care of doing deep copies of your data structure you ran into an inconsistent state. In Elixir, there is no such thing as shared mutable state, so deep copy comes for free &#x1F60E;</li><li><strong>Immutability is slower. </strong>The cost of safety is that any write operation in Elixir will be slower than in any other languages with mutable data structures. <code><a href="https://erlang.org/doc/man/ets.html?ref=bits-of-code-pieces-of-writing">ets</a></code> or <code><a href="https://erlang.org/doc/man/atomics.html?ref=bits-of-code-pieces-of-writing">atomics</a></code> Erlang APIs can sometimes be handy.</li></ul><h2 id="final-words"><strong><strong>Final words</strong></strong></h2><p>If you didn&apos;t know Advent Of Code before or just never tried, I hope this post will convince you to give a shot at it. AOC is not timebound and you can follow any event whenever you want at your own pace.</p><p>Oh, I forgot to mention that AOC is also a bit addictive and I may have started over from year 2015 &#x1F64A; &#x1F601; &#x1F384;</p>]]></content:encoded></item><item><title><![CDATA[On Elixir Metaprogramming]]></title><description><![CDATA[Metaprogramming is a scary word: it sounds like voodoo for programmers, and in some manners it is. It is used by most popular Elixir libraries and as I leveled up as an Elixir programmer, I needed to understand how it worked under the hood. ]]></description><link>https://www.christianblavier.com/on-elixir-metaprogramming/</link><guid isPermaLink="false">629603e163e1200001252ac3</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Christian Blavier]]></dc:creator><pubDate>Mon, 23 Nov 2020 17:00:00 GMT</pubDate><media:content url="https://www.christianblavier.com/content/images/2020/11/inception-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.christianblavier.com/content/images/2020/11/inception-2.jpg" alt="On Elixir Metaprogramming"><p><em>Metaprogramming</em> is a scary word: it sounds like voodoo for programmers, and in some manners it is. 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. </p><p>I personally felt intimidated by the topic, and I owned the <a href="https://pragprog.com/titles/cmelixir/metaprogramming-elixir/?ref=bits-of-code-pieces-of-writing"><em>Metaprogramming Elixir</em> book</a> for more than a year before actually reading it &#x1F631;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2020/11/elixir-books.jpg" class="kg-image" alt="On Elixir Metaprogramming" loading="lazy" width="1200" height="900" srcset="https://www.christianblavier.com/content/images/size/w600/2020/11/elixir-books.jpg 600w, https://www.christianblavier.com/content/images/size/w1000/2020/11/elixir-books.jpg 1000w, https://www.christianblavier.com/content/images/2020/11/elixir-books.jpg 1200w" sizes="(min-width: 720px) 720px"><figcaption>My Elixir personal library&#xA0;</figcaption></figure><p>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&#x2019;s introductory advice:</p><blockquote>Rule 1 : Don&apos;t Write Macros - Chris McCord</blockquote><p>Be cautious with <em>metaprogramming</em> as it is hardly maintainable and a pain to debug; don&apos;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.</p><h2 id="it-s-a-kind-of-magic">It&apos;s a kind of magic</h2><p>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!</p><figure class="kg-card kg-code-card"><pre><code class="language-ruby">irb&gt; 2.years.from_now + 5.days.from_now 
#=&gt; Mon, 28 Nov 2022 13:38:36 UTC

irb&gt; Person.find_by_user_name_and_password(user_name, password)
#=&gt; #&lt;Person id: 10, user_name: &quot;Ryan&quot;&gt;</code></pre><figcaption>Ruby magic in action</figcaption></figure><p>Rails made indeed an intensive usage of Ruby dynamic features to extend the language at runtime: the <code>find_by_user_name_and_password</code> call &#xA0;just above is an example of dynamically generated class method based on the <code>Person</code> model attributes.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2020/11/90c9a1015850dff7dece6249e4a25eef.jpg" class="kg-image" alt="On Elixir Metaprogramming" loading="lazy" width="1024" height="562" srcset="https://www.christianblavier.com/content/images/size/w600/2020/11/90c9a1015850dff7dece6249e4a25eef.jpg 600w, https://www.christianblavier.com/content/images/size/w1000/2020/11/90c9a1015850dff7dece6249e4a25eef.jpg 1000w, https://www.christianblavier.com/content/images/2020/11/90c9a1015850dff7dece6249e4a25eef.jpg 1024w" sizes="(min-width: 720px) 720px"><figcaption>How I felt when learning Ruby on Rails</figcaption></figure><p>But there are caveats to Ruby magic:</p><ul><li><a href="https://stackoverflow.com/questions/441717/whats-wrong-with-magic?ref=bits-of-code-pieces-of-writing">too much magic</a> may hurt as you lack the understanding of what really happens under the hood. Same logic also applies to Elixir <em>metaprogramming</em> (remember Chris McCord&apos;s motto? &quot;<em>Don&apos;t Write Macros&quot; </em>&#x1F605;)</li><li>Ruby magic is <strong>dynamic</strong> by nature which means it is performed at runtime, at the expense of performance. Whereas <strong>Elixir macros are evaluated at compile time</strong> and inserted in your bytecode as if it were manually written code.</li></ul><h2 id="what-is-metaprogramming">What is metaprogramming?</h2><p>Before going deeper in the subject, it&#x2019;s worth reminding that <em>metaprogramming</em> is the art of writing code that will generate code. <em>Metaprogramming</em> features are first class members of the Elixir language which provides macro API to rapidly extend the core language.</p><p>Did you know that <em>if-then-else</em> or <em>unless</em> block syntax are written in Elixir through its macro mechanism? It provides syntaxic sugar over a binary case pattern matching structure.</p><figure class="kg-card kg-code-card"><pre><code class="language-elixir">defmacro macro_unless(clause, do: expression) do
  quote do
    if(!unquote(clause), do: unquote(expression))
  end
end</code></pre><figcaption>unless block written with an Elixir macro</figcaption></figure><p>You could also be using meta-programmed features everyday without knowing it:</p><ul><li><a href="https://github.com/elixir-lang/elixir/tree/master/lib/ex_unit?ref=bits-of-code-pieces-of-writing">ExUnit</a> test definitions leverages on macros</li><li><a href="https://github.com/elixir-ecto/ecto/blob/master/lib/ecto/query.ex?ref=bits-of-code-pieces-of-writing">Ecto querying DSL</a> can perfectly mimic SQL language thanks to <em>metaprogramming</em></li><li>Of course Chris McCord&#x2019;s Phoenix framework uses macro a lot, like the <a href="https://hexdocs.pm/phoenix/routing.html?ref=bits-of-code-pieces-of-writing">Routing DSL</a></li></ul><p><strong>By the way, did you know that more than 90% of the Elixir sourcecode is written in Elixir? Meta enough for you? &#x1F92F;</strong></p><h2 id="elixir-macros-101">Elixir Macros 101</h2><p>Your first Elixir macros will be a bit tedious to write. I won&#x2019;t give a full course about Elixir <em>metaprogramming</em> (buy <a href="https://pragprog.com/titles/cmelixir/metaprogramming-elixir/?ref=bits-of-code-pieces-of-writing">Chris McCord&#x2019;s book</a>!) but I will instead give you a few keys to understand it.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-21-at-08.29.12@2x.png" class="kg-image" alt="On Elixir Metaprogramming" loading="lazy" width="1182" height="208" srcset="https://www.christianblavier.com/content/images/size/w600/2020/11/CleanShot-2020-11-21-at-08.29.12@2x.png 600w, https://www.christianblavier.com/content/images/size/w1000/2020/11/CleanShot-2020-11-21-at-08.29.12@2x.png 1000w, https://www.christianblavier.com/content/images/2020/11/CleanShot-2020-11-21-at-08.29.12@2x.png 1182w" sizes="(min-width: 720px) 720px"><figcaption>Can&apos;t blame <a href="https://twitter.com/matt_chabert?ref=bits-of-code-pieces-of-writing">Matthieu</a>, happened to me as well!</figcaption></figure><p>The main thing you need to understand is that all code you write in Elixir is represented in memory as an <em>Abstract Syntax Tree</em> (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.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2020/11/Untitled-2020-11-20-0920.png" class="kg-image" alt="On Elixir Metaprogramming" loading="lazy" width="1938" height="810" srcset="https://www.christianblavier.com/content/images/size/w600/2020/11/Untitled-2020-11-20-0920.png 600w, https://www.christianblavier.com/content/images/size/w1000/2020/11/Untitled-2020-11-20-0920.png 1000w, https://www.christianblavier.com/content/images/size/w1600/2020/11/Untitled-2020-11-20-0920.png 1600w, https://www.christianblavier.com/content/images/2020/11/Untitled-2020-11-20-0920.png 1938w" sizes="(min-width: 720px) 720px"><figcaption>Abstract Syntax Tree (AST)</figcaption></figure><p><em>Metaprogramming</em>, which we previously defined as &#xAB; code to generate code &#xBB; could be achieved by generating raw Elixir code (as strings) but it would be unefficient and hardly maintainable. </p><p>That&#x2019;s why Elixir provides API&#x2019;s to generate and evaluate AST. Here comes <code>quote</code>: an Elixir primitive which can convert any Elixir code into its AST.</p><pre><code class="language-elixir">iex(1)&gt; quote do 5 + 8 * 3 end
#=&gt; {:+, [context: Elixir, import: Kernel],
#=&gt;   [5, {:*, 
#=&gt;     [context: Elixir, import: Kernel], 
#=&gt;     [8, 3]
#=&gt;   ]}]
#=&gt; }</code></pre><p>This code can be evaluated the same way you would have evaluated code in a string (think <code>eval</code> in <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval?ref=bits-of-code-pieces-of-writing">Javascript</a>)</p><pre><code class="language-elixir">iex(1)&gt; Code.eval_string(&quot;5 + 8 * 3&quot;)
#=&gt; {29, []}
iex(2)&gt; Code.eval_quoted(quote do 5 + 8 * 3 end)
#=&gt; {29, []}</code></pre><p>Now what happens if you try to evaluate code with dynamic parts? </p><pre><code class="language-elixir">iex(1)&gt; a = 5
iex(2)&gt; Code.eval_string(&quot;3 * a&quot;)
#=&gt; ** (CompileError) nofile:1: undefined function a/0</code></pre><p>Of course <code>a</code> is unknown to your evaluated code and a simple way to fix this would be to use text interpolation.</p><pre><code class="language-elixir">iex(1)&gt; a = 5
iex(2)&gt; Code.eval_string(&quot;3 * #{a}&quot;)
#=&gt; {15, []}</code></pre><p>Well the good news is you can interpolate within your quoted code as well: use the <code>unquote</code> primitive.</p><pre><code class="language-elixir">iex(1)&gt; a = 5
iex(2)&gt; Code.eval_quoted(quote do 3 * unquote(a) end)
#=&gt; {15, []}</code></pre><p>Simple as that! So whenever you think you are lost between your <code>quote</code> / <code>unquote</code> blocks, just look at it as if it were text interpolation. </p><p>Then you need to know about <code>defmacro</code> which you probably already encountered in your Phoenix project. It&apos;s a special construct able to produce code, from quoted expressions and parameters.</p><p>Here is an example that generates a few functions :</p><pre><code class="language-elixir">defmodule MyMacro do
  defmacro generate_functions(name_and_values) do
    for {name, value} &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)&gt; MyModule.two + MyModule.three 
#=&gt; 5</code></pre><p>There are quite a few other things to know when it comes to Elixir <em>metaprogramming</em>, but <code>quote</code>, <code>unquote</code> and <code>defmacro</code> are the basics and can already help you to build powerful stuff.</p><h2 id="ok-but-what-for">Ok, but what for?</h2><p>I will end this post with some examples of the way we use <em>metaprogramming</em> in our Elixir projects.</p><h3 id="extends-ecto-query-api">Extends Ecto query API</h3><p>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.</p><figure class="kg-card kg-code-card"><pre><code class="language-elixir">iex(1)&gt; query = from u in User, where: u.age &gt; 0, select: u.name
iex(2)&gt; Repo.all(query)</code></pre><figcaption>Typical Ecto query</figcaption></figure><p>But every now and then you&apos;ll want to use some specific database functions not available in Ecto API. You can then use <code>fragment</code> Ecto function which let you insert raw SQL in your Ecto Query:</p><figure class="kg-card kg-code-card"><pre><code class="language-elixir">query = 
  from u in User,
  where: is_nil(fragment(&quot;?-&gt;&gt;?&quot;, u.metadata, &quot;phone&quot;))</code></pre><figcaption>Using fragment to access a field within a Postgres JSONB column</figcaption></figure><p>But the proper way is to leverage on macros to extend the Ecto query API:</p><figure class="kg-card kg-code-card"><pre><code class="language-elixir">defmodule MyQueryMacros do
  defmacro jsonb_get(column, key) do
    quote do
      fragment(&quot;?-&gt;&gt;?&quot;, unquote(column), unquote(key))
    end
  end
 end
 
 query = 
   from u in User,
   where: is_nil(jsonb_get(u.metadata, &quot;phone&quot;))</code></pre><figcaption>Same Ecto query using our homemade jsonb_get macro</figcaption></figure><h3 id="aliases-imports">Aliases &amp; Imports</h3><p>As your Elixir codebase grows, you can see that you repeat over and over the same <code>alias</code> and <code>import</code> blocks at the head of your files. Not really <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself?ref=bits-of-code-pieces-of-writing">DRY</a>...</p><p>Phoenix already offers a solution you can use through the <code>use</code> / <code>__using__</code> macro. To declare a <em>controller</em> or a <em>view, </em>phoenix suggests a bunch of default aliases and imports declared in <code>my_app_web.ex</code> file. Here is an example of <a href="https://changelog.com/?ref=bits-of-code-pieces-of-writing">changelog.com</a> <code>*_web.ex</code> file :</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/thechangelog/changelog.com/blob/master/lib/changelog_web.ex?ref=bits-of-code-pieces-of-writing"><div class="kg-bookmark-content"><div class="kg-bookmark-title">thechangelog/changelog.com</div><div class="kg-bookmark-description">Hacker to the &#x1F49A;. Contribute to thechangelog/changelog.com development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="On Elixir Metaprogramming"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">thechangelog</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars0.githubusercontent.com/u/161466?s=400&amp;v=4" alt="On Elixir Metaprogramming"></div></a></figure><p>You can then use the macro with the following snippet, which will <em>alias</em> and <em>import</em> a few Phoenix modules and even generate some authorization related functions:</p><pre><code class="language-elixir">defmodule MyAppWeb.MyController do
  use MyAppWeb, :controller
  
  # ...
end</code></pre><p>In our own project, we also brewed our own <em>aliasing</em> system that we can use like this to prevent us from declaring the same aliases over and over.</p><pre><code class="language-elixir">defmodule MyApp.Contracts.SomeContractService do
  use MyApp,
    aliases: [:campaigns, :contracts, :users],
    private_aliases: [:contracts]
    
  # ...
end   </code></pre><p>I may write further about these specific macros in another post &#x1F609;</p><h3 id="changix">changix</h3><p>A final example of how we use macros in our main Elixir application is our <strong>application changelog</strong>. We present new features, bug fixes and improvement to our users with a changelog sidebar (and a blinking badge to draw their attention).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2020/11/changelog.jpg" class="kg-image" alt="On Elixir Metaprogramming" loading="lazy" width="1600" height="1100" srcset="https://www.christianblavier.com/content/images/size/w600/2020/11/changelog.jpg 600w, https://www.christianblavier.com/content/images/size/w1000/2020/11/changelog.jpg 1000w, https://www.christianblavier.com/content/images/2020/11/changelog.jpg 1600w" sizes="(min-width: 720px) 720px"><figcaption>A changelog sidebar embedded in our webapp</figcaption></figure><p>Instead of feeding this changelog from database, we use <em>markdown</em> files with a <a href="https://jekyllrb.com/docs/front-matter/?ref=bits-of-code-pieces-of-writing">YAML front matter</a> header.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2020/11/markdown.jpg" class="kg-image" alt="On Elixir Metaprogramming" loading="lazy" width="1600" height="985" srcset="https://www.christianblavier.com/content/images/size/w600/2020/11/markdown.jpg 600w, https://www.christianblavier.com/content/images/size/w1000/2020/11/markdown.jpg 1000w, https://www.christianblavier.com/content/images/2020/11/markdown.jpg 1600w" sizes="(min-width: 720px) 720px"><figcaption>Our markdown changelog files, commited right along source code</figcaption></figure><p>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.</p><p>But after reading Chris McCord&apos;s book, we realized that these <em>markdown</em> files could be considered as source code and be compiled as well (or at least processed at compile-time). Here comes <a href="https://github.com/cblavier/changix?ref=bits-of-code-pieces-of-writing"><strong>changix</strong></a>: a library relying on macros to provide compile-time changelog features. </p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/cblavier/changix?ref=bits-of-code-pieces-of-writing"><div class="kg-bookmark-content"><div class="kg-bookmark-title">cblavier/changix</div><div class="kg-bookmark-description">Compile-time changelog features for Elixir. Contribute to cblavier/changix development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="On Elixir Metaprogramming"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">cblavier</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars3.githubusercontent.com/u/130783?s=400&amp;v=4" alt="On Elixir Metaprogramming"></div></a><figcaption>Help yourself, it&apos;s open-source &#x1F600;</figcaption></figure><h2 id="final-words">Final words</h2><p>I hope that this post will help you feel more confortable with Elixir <em>metaprogramming</em> and give you enough courage to hack yours; but remember Chris&apos;s advice and always be cautious &#x1F605;</p>]]></content:encoded></item><item><title><![CDATA[Boring is the new Black]]></title><description><![CDATA[ In this article I will explain why I believe that boring - yet pragmatic - choices are often the most powerful.]]></description><link>https://www.christianblavier.com/boring-is-the-new-black/</link><guid isPermaLink="false">629603e163e1200001252ac2</guid><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Christian Blavier]]></dc:creator><pubDate>Wed, 26 Feb 2020 10:39:45 GMT</pubDate><media:content url="https://www.christianblavier.com/content/images/2020/02/sebastian-unrau-sp-p7uuT0tw-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.christianblavier.com/content/images/2020/02/sebastian-unrau-sp-p7uuT0tw-unsplash.jpg" alt="Boring is the new Black"><p>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.</p><h2 id="did-you-say-boring">Did you say &quot;<strong>boring&quot;</strong>?</h2><p>I borrowed this term from a talk given by two Doctolib software engineers (<a href="https://www.doctolib.fr/?ref=bits-of-code-pieces-of-writing">Doctolib</a> is a very successful french startup in the health space). In this <a href="https://fr.slideshare.net/OCTOTechnology/la-duck-conf-the-boring-architecture?ref=bits-of-code-pieces-of-writing">presentation</a>, Nicolas &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.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2019/12/CleanShot-2019-12-10-at-22.35.15@2x.png" class="kg-image" alt="Boring is the new Black" loading="lazy"><figcaption>Doctolib software architecture</figcaption></figure><p>Before going deeper into software architecture, you may want to know that the <em>boring trend</em> has also been mentioned in quite a few other fields:</p><ul><li>LA Times journalist Christopher Hawthorne is praising boring<strong> building </strong>architecture in his <a href="https://www.latimes.com/entertainment/arts/la-ca-cm-building-type-boring-buildings-20171119-htmlstory.html?ref=bits-of-code-pieces-of-writing">&quot;Boring architecture? Yes please&quot;</a> paper.</li><li>Nick Rollins helps us to remember that a <a href="https://uxengineer.com/good-ux-boring-ui/?ref=bits-of-code-pieces-of-writing">Good User Experience involves a Boring User Interface</a> and urges not be creative when dealing with UI: don&apos;t mess with your users and just stick to the - <em>boring</em> - established standards.</li><li>And even Elon Musk named his company <a href="https://www.boringcompany.com/?ref=bits-of-code-pieces-of-writing">The Boring Company</a> ;-) (terrible example, they are actually boring, ie. drilling, tunnels)</li></ul><h2 id="boring-architecture">Boring architecture</h2><p>Let&apos;s focus again on software architecture: a few years ago I <a href="https://www.christianblavier.com/rails-with-no-js-framework/">wrote an article</a> 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.</p><p>Unfortunately it seems that my ideas are against the current flow, since average web applications kept getting more and more complex with:</p><ul><li>a lot of different <strong>storage solutions</strong> (NoSQL)</li><li><strong>GraphQL</strong> replacing REST APIs</li><li>The rise of <strong>micro-services architectures </strong>(M/SOA)</li><li><strong>Containerized</strong> (Docker) and <strong>orchestrated deployments</strong> (Kubernetes)</li></ul><p>These architectural solutions are impressive at helping &#xA0;large scale companies to deal with their storage or operation issues. <strong>But what drives me crazy</strong> is to see how many, seed or low-scale companies / softwares <strong>start using these technologies from the ground-up</strong>.</p><h3 id="micro-services-architectures">Micro Services architectures</h3><p>Let&apos;s take an example : <strong>Micro Services</strong>. It looks so neat and clever on your PowerPoint slides, making you think you will <em>divide and conquer</em> on your software by splitting the big business problems you are trying to solve in much smaller pieces of software - easier to manage.</p><p>But M/SOA also induces a very important development overhead: </p><ul><li>If you want your micro-services to talk together, you&apos;ll have to tie them up with APIs (whereas a simple function call would have been much simpler and faster).</li><li>You can&apos;t simply share code between your services. You&apos;ll have to build and release libraries shared between your micro-services, even for sharing a simple one-liner function across your services.</li><li>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.</li><li>Versioning and release-management is really tough.</li></ul><p>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.</p><h3 id="document-databases">Document databases</h3><p>Another example is the use of <strong>document oriented databases</strong> (such as MongoDB or CouchDB). Such databases are designed with very attractive benefits compared to relational databases: </p><ul><li>you can store document (think <em>JSON object</em>) instead of simples rows.</li><li>database is schema-less meaning a single collection (ie. <em>table</em>) can hold documents of different nature (new attributes ...) without any schema migration involved.</li></ul><p>Unfortunately, what these databases are not marketed for, is the feature they lack regarding SQL databases:</p><ul><li>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 <a href="https://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem-in-orm-object-relational-mapping?ref=bits-of-code-pieces-of-writing">N+1 queries</a> or de-normalized data structures which are a nightmare to maintain, making you build over-engineered <a href="https://martinfowler.com/bliki/CQRS.html?ref=bits-of-code-pieces-of-writing">CQRS</a> architectures (overhead!)</li><li>no <a href="https://en.wikipedia.org/wiki/ACID?ref=bits-of-code-pieces-of-writing">ACID transaction</a>. 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</li></ul><figure class="kg-card kg-image-card"><img src="https://www.christianblavier.com/content/images/2020/02/nosql-small.jpg" class="kg-image" alt="Boring is the new Black" loading="lazy"></figure><p>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.</p><p>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).</p><h2 id="the-majestic-monolith">The Majestic Monolith</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.christianblavier.com/content/images/2019/12/1C0WR8CjuV3MCbuukjOqeUg.png" class="kg-image" alt="Boring is the new Black" loading="lazy"><figcaption><a href="http://reneaigner.deviantart.com/art/Monolith-283096035?ref=bits-of-code-pieces-of-writing">Monolith, by Rene Aigner</a></figcaption></figure><p>Monolithic software architectures sound awkward, clumsy - of course <em>boring</em> - and unable to face agile and fast changing requirements. But they are quite the opposite.</p><p>Doctolib previous example has already proven me right, but so did <a href="https://basecamp.com/?ref=bits-of-code-pieces-of-writing">Basecamp</a>. In <a href="https://m.signalvnoise.com/the-majestic-monolith/?ref=bits-of-code-pieces-of-writing">The Majestic Monolith</a> post DHH explains how Basecamp is dealing with million of users with their large and monolithic application.</p><blockquote>Run a small team, not a tech behemoth? Embrace the monolith and make it majestic. You Deserve It! - DHH, 2019</blockquote><p>Building your software as a monolith doesn&apos;t mean that you don&apos;t care about architecture and are willing to build a big plate of <em>spaghetti-ware</em>. 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.</p><p><strong>You can totally craft well-thought and battle-tested code in a - majestic - monolithic fashion</strong></p><h2 id="boring-organizations">Boring organizations</h2><p>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 <strong>started building less complicated software, </strong>you might figure out that <strong>you need smaller teams?</strong></p><blockquote><em>&quot;Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations&quot; &#xA0;- </em>Conway, 1967.</blockquote><p>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 <strong>small teams</strong>, hire <strong><em>jack-of-all-trades </em>full-stack developers</strong> and <strong>build clean, simple and pragmatic software</strong>. I&apos;m quite sure this advice should resonate with most companies building software ; not everyone is building Google or Facebook.</p><blockquote>The patterns that make sense for organizations orders of magnitude larger than yours, are often the exact opposite ones that&#x2019;ll make sense for you. It&#x2019;s the essence of cargo culting. If I dance like these behemoths, surely I too will grow into one. I&#x2019;m sorry, but that&#x2019;s just not how the tango goes. - DHH, 2019</blockquote><h2 id="wrapping-up">Wrapping up</h2><p>I don&apos;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.</p><p>You might wonder <em>&quot;Will I get bored at building boring stuff?</em> . I don&apos;t think you will:</p><ul><li>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.</li><li>I&apos;m still tech savvy and really interested in new technologies and frameworks, but I&apos;m also getting older and wiser ;-) When considering a new technology I&apos;m always thinking about the tradeoff between the provided value and the induced overhead.</li></ul>]]></content:encoded></item><item><title><![CDATA[How we improved our Elixir build speed by 5x]]></title><description><![CDATA[<p><em><em>I&#x2019;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 </em></em><a href="https://medium.com/u/f31378845318?source=post_page-----d45393c6700f----------------------" rel="noopener"><em><em>Jos&#xE9; Valim</em></em></a><em><em> to add a </em></em><a href="https://github.com/elixir-lang/elixir/pull/9422?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow"><em><em>test partitioning feature</em></em></a></p>]]></description><link>https://www.christianblavier.com/how-we-improved-our-elixir-build-speed-by-5x/</link><guid isPermaLink="false">629603e163e1200001252ac1</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[CI]]></category><dc:creator><![CDATA[Christian Blavier]]></dc:creator><pubDate>Tue, 08 Oct 2019 20:53:00 GMT</pubDate><media:content url="https://www.christianblavier.com/content/images/2019/12/1__NyTuevjXFDbns3htQrN3g.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://www.christianblavier.com/content/images/2019/12/1__NyTuevjXFDbns3htQrN3g.jpeg" alt="How we improved our Elixir build speed by 5x"><p><em><em>I&#x2019;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 </em></em><a href="https://medium.com/u/f31378845318?source=post_page-----d45393c6700f----------------------" rel="noopener"><em><em>Jos&#xE9; Valim</em></em></a><em><em> to add a </em></em><a href="https://github.com/elixir-lang/elixir/pull/9422?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow"><em><em>test partitioning feature</em></em></a><em><em> to Elixir</em></em> &#x1F917;</p><hr><h1 id="some-context">Some context</h1><p>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.</p><p>Since we&#x2019;re deploying our app on Heroku, we&#x2019;ve been using:</p><ul><li><strong><strong>SemaphoreCI as our CI tool</strong></strong> (because <a href="https://semaphoreci.com/?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">SemaphoreCI</a> is available as a Heroku add-on, simple as that!)</li><li><strong><strong>Heroku native pipelines to perform CD</strong></strong> whenever CI is successful on our Git master branch</li></ul><p>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 <strong><strong>we had to wait almost for 1 hour before being able to deploy our code to staging</strong></strong>: 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!</p><p>In this article, we&#x2019;ll talk about the actions we took in order to <strong><strong>improve an unreliable</strong></strong> &#x2014; randomly failing &#x2014; <strong><strong>15 minutes long CI process </strong></strong>to a <strong><strong>solid and steady 3 minutes build.</strong></strong></p><h1 id="-1-improve-worst-tests">#1 &#x2014; Improve worst tests</h1><p>First action is to find out the culprits: which tests are performing really slowly and which are failing randomly?</p><p>Running <code>mix test --trace</code> is very useful as it gives you detailed execution time, per test. But my favorite is <code>mix test --slowest 10</code> 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.</p><p>Regarding randomly failing tests, two things can really help:</p><ul><li>a simple <strong><strong>bash loop</strong></strong> to run a single test over and over. If it returns anything else than 0, the test is unreliable.</li></ul><!--kg-card-begin: markdown--><pre><code class="language-sh">(for i in {1..10}; do mix test test/lib/my_app/features/my_browser_test.exs:10 &gt; /dev/null; echo $?; done) | paste -s -d+ - | bc
</code></pre>
<!--kg-card-end: markdown--><ul><li>a <code>wait_until</code> function that helps to fix unreliable browser tests (we&apos;re using <a href="https://github.com/HashNuke/hound?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">Hound</a> with <em><em>chromedriver</em></em>). Here is the <a href="https://gist.github.com/cblavier/1acccfd914e4cd6a51af0cf500dff6f8?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">gist</a>.</li></ul><p>A last ugly trick &#x1F648; I am ashamed of and I urge you not to use is to give another chance to your failing tests! Running your tests with <code>mix test || mix test --failed</code> will automatically rerun any failed test and still give you a 0 exit code if it passes on the second time.</p><figure class="kg-card kg-image-card"><img src="https://www.christianblavier.com/content/images/2019/12/cat.gif" class="kg-image" alt="How we improved our Elixir build speed by 5x" loading="lazy"></figure><p><em><em>Pro tip: I strongly encourage you to read </em></em><code><em><em>mix help test</em></em></code><em><em> output: hidden gems in there!</em></em></p><h1 id="-2-cache-all-things">#2 &#x2014; Cache all things</h1><p>At this stage, the build became way more reliable (99% success rate) but still really slow (~ 12 min).</p><p>My first attempt to improve this was to see if the grass was greener at GitHub&#x2019;s with their brand new <a href="https://github.com/features/actions?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">GitHub Actions</a>, and if it would result in an improved build duration. Although promising, I did not manage to run any of our <em><em>chromedriver </em></em>tests with their product, and execution time was anyway quite similar.</p><p>I then decided to go back to Semaphore and migrate from the classic plan to their new <a href="https://semaphoreci.com/?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">SemaphoreCI 2.0</a>. The new Semaphore (like GitHub Actions <em><em>btw</em></em>) 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 <a href="https://docs.semaphoreci.com/article/149-caching?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">cache commands</a>.</p><p>By using <code>cache store</code> and <code>cache restore</code> at strategic locations you can make most of your builds way faster:</p><ul><li>by not re-downloading Elixir dependencies</li><li>not re-compiling all Elixir code, only updated code</li><li>not re-downloading NPM/Yarn dependencies</li></ul><p><em><em>Pro tip: have a look at fail_fast and auto_cancel Semaphore settings. Real time savers!</em></em></p><h1 id="-3-leverage-on-parallelism">#3 &#x2014;Leverage on parallelism</h1><p>Caching really helped, and at this step we could have stopped improving the process as our build time already <strong><strong>went down to 6 minutes.</strong></strong></p><p>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!</p><p>Our build was really straight-forward then:</p><figure class="kg-card kg-image-card"><img src="https://www.christianblavier.com/content/images/2019/12/semaphore1.png" class="kg-image" alt="How we improved our Elixir build speed by 5x" loading="lazy"></figure><p>The first improvement is to run in parallel what can be done in parallel:</p><ul><li><code>mix credo</code> (the Elixir code analysis tool) and <code>mix test</code> can be run at the same time. With a fail-fast strategy that stops running tests whenever <em><em>credo</em></em> fails, it can be a real time saver!</li><li>JS &amp; CSS assets can be compiled at the same time than Elixir code: it can save up to one minute.</li></ul><p>The final step is to split our tests in different batches to also run them in parallel. I wrote this <a href="https://gist.github.com/cblavier/4e6fa2939145bfb56a02b2bd413fec60?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">custom test runner</a> (which simply wraps <code>mix test</code> shell command) that splits all test files in N batches (by hashing their filename) and only runs a single batch out of N:</p><!--kg-card-begin: markdown--><pre><code class="language-shell"># Run all tests in 4 random batches
$&gt; mix n_test 1 4
$&gt; mix n_test 2 4
$&gt; mix n_test 3 4
$&gt; mix n_test 4 4
</code></pre>
<!--kg-card-end: markdown--><p>You can then leverage on SemaphoreCI <a href="https://docs.semaphoreci.com/article/50-pipeline-yaml?ref=bits-of-code-pieces-of-writing#parallelism" rel="noopener nofollow">parallelism</a> to run a single task N times with an incremental index passed as an environment variable.</p><p>Our CI finally looks like this, running at a solid and steady 3mn pace!</p><figure class="kg-card kg-image-card"><img src="https://www.christianblavier.com/content/images/2019/12/semaphore2.png" class="kg-image" alt="How we improved our Elixir build speed by 5x" loading="lazy"></figure><p><em><em>Pro tip: save time at learning Semaphore by reading our </em></em><a href="https://gist.github.com/cblavier/db7fb947aa8158449608557faafb1216?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow"><em><em>build pipeline</em></em></a><em><em> setup</em></em></p><h1 id="wrapping-up">Wrapping up</h1><p>It has been known <a href="https://martinfowler.com/articles/continuousIntegration.html?ref=bits-of-code-pieces-of-writing" rel="noopener nofollow">for years</a> how important Continuous Integration is, for any size of software team. But it should be a source of trust, <a href="https://rubyonrails.org/doctrine/?ref=bits-of-code-pieces-of-writing#optimize-for-programmer-happiness" rel="noopener nofollow">happiness</a> and increased productivity, never a source of frustration!</p><p>So start paying attention, and maybe some of the guidelines in this post will help you at improving your CI build!</p><p><em><em>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; parallelism.</em></em></p>]]></content:encoded></item><item><title><![CDATA[Rails with no JS framework]]></title><description><![CDATA[<h3 id="because-all-apps-don-t-need-ember-or-angular">Because all apps don&#x2019;t need Ember or Angular</h3><p>I&#x2019;m writing code for over a decade now, consider myself as a geek, and as a geek I&#x2019;m always very curious and excited about any new fancy technology released every now and then.</p><p>These days client-side</p>]]></description><link>https://www.christianblavier.com/rails-with-no-js-framework/</link><guid isPermaLink="false">629603e163e1200001252ac0</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[Ruby]]></category><dc:creator><![CDATA[Christian Blavier]]></dc:creator><pubDate>Fri, 28 Feb 2014 21:57:00 GMT</pubDate><media:content url="https://www.christianblavier.com/content/images/2019/12/library.jpeg" medium="image"/><content:encoded><![CDATA[<h3 id="because-all-apps-don-t-need-ember-or-angular">Because all apps don&#x2019;t need Ember or Angular</h3><img src="https://www.christianblavier.com/content/images/2019/12/library.jpeg" alt="Rails with no JS framework"><p>I&#x2019;m writing code for over a decade now, consider myself as a geek, and as a geek I&#x2019;m always very curious and excited about any new fancy technology released every now and then.</p><p>These days client-side MVC frameworks are very trendy, and to be honest what I read and learned regarding <em>Ember</em> or <em>Angular </em>(among many others) looked really shiny and well-thought. These are typically frameworks that I would love to love.</p><p>But right now, I don&#x2019;t need them. <strong>Do you?</strong></p><hr><h3 id="the-playground">The playground</h3><p>In addition to my freelancing activity, I&#x2019;m running <a href="http://www.folyo.me/?ref=bits-of-code-pieces-of-writing" rel="noopener">Folyo</a> (which by the way, can help you to find great designer talents). It&#x2019;s as <em>Ruby on Rails</em> website with a UI that could be defined as slick, reactive and responsive (especially thanks to <a href="http://sachagreif.com/?ref=bits-of-code-pieces-of-writing" rel="noopener">Sacha</a>, my co-founder), but <strong>by no means complex</strong>.</p><p>As a job board, Folyo has quite a common <em>page driven architecture</em> where any action performed on a page will often lead you to another page or refresh current page content. Folyo&#x2019;s UI <strong>does not involve rich interactions</strong> like <em>live data charts</em> or <em>dynamic data binding. </em>No, just web pages and it&#x2019;s perfectly fine!</p><h4 id="so-what-about-client-side-frameworks">So what about client-side frameworks?</h4><p>Why not, since it seems like the way to go these days?</p><p>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 <a href="https://sourcegraph.com/blog/switching-from-angularjs-to-server-side-html?ref=bits-of-code-pieces-of-writing" rel="noopener">this</a>) and also have a steep learning curve.</p><p>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.</p><p>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:</p><p><em>Put whatever you want in application.js.</em></p><p><strong>Eh. Really?</strong></p><h3 id="time-to-clean-it-up">Time to clean it up</h3><p>I will now explain the way we decided to organize Javascript code on <a href="http://www.folyo.me/?ref=bits-of-code-pieces-of-writing" rel="noopener">Folyo</a>, in a much more manageable way than <em>everything in application.js</em>. I believe it&#x2019;s quite pragmatic and I&#x2019;m sure it will fit with a lot of other applications.</p><p>Required libraries:</p><ul><li><em>jQuery</em>, obviously.</li><li><em>Coffeescript</em>, not mandatory but its syntax for class definition is very convenient.</li><li><em>Turbolinks. </em>It will really improve user experience by making navigation from page to page AJAX only.</li><li><em>HeadJS, </em>used through <em>headjs-rails </em>gem, will speed up your app by loading your JS asynchronously.</li></ul><h4 id="code-hierarchy">Code hierarchy</h4><p>To organize the code, I will simply obey to following rules:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-images-1.medium.com/max/800/1*UpRTNIf-ll1ovWBUtolpJA.png" class="kg-image" alt="Rails with no JS framework" loading="lazy"><figcaption>Folyo JS code hierarchy</figcaption></figure><ul><li>Any page requiring JS code will have it&#x2019;s own class (a JS view).</li><li>If you need to share piece of code between views, put it in a widget class.</li><li><em>application.js</em> is just doing the glue between your classes, jQuery, HeadJS and Turbolinks.</li><li>That&#x2019;s all!</li></ul><h4 id="views">Views</h4><p>ApplicationView is the default view, inherited by any other view, and instantiated by default when no specific view is provided.</p><!--kg-card-begin: markdown--><pre><code class="language-javascript">window.Views ||= {}
class Views.ApplicationView

 render: -&gt;
   Widgets.FancyBox.enable()
   Widgets.MarkdownEditor.enable()
   
 cleanup: -&gt;
   Widgets.FancyBox.cleanup()
   Widgets.MarkdownEditor.cleanup()
</code></pre>
<!--kg-card-end: markdown--><p>Since we want <em>FancyBox</em> and our <em>Markdown</em> editor to work on a lot of pages, we put it in the <em>ApplicationView</em> as a default behavior.</p><p>Then a typical view would look like that:</p><!--kg-card-begin: markdown--><pre><code class="language-javascript">window.Views.Newsletters ||= {}
class Views.Newsletters.EditView extends Views.ApplicationView

 render: -&gt;
   super()
   $(&apos;a.preview&apos;).click (e) -&gt;
     e.preventDefault()
     url = $(e.target).attr(&apos;href&apos;)
     window.open(url, &apos;_blank&apos;, &apos;width=800,height=800&apos;)
     
 cleanup: -&gt;
   super()
</code></pre>
<!--kg-card-end: markdown--><p>Simple as that! But why the cleanup stuff?</p><p>Because with <em>Turbolinks, </em>the Javascript environment is not reset between each page. For instance, if you define a<em> </em>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)</p><h4 id="widgets"><strong>Widgets</strong></h4><p>Sorry, no rocket science here.</p><!--kg-card-begin: markdown--><pre><code class="language-javascript">window.Widgets ||= {}

class Widgets.FancyBox
  @enable:  -&gt; $(&quot;.fancybox&quot;).fancybox()
  @cleanup: -&gt; $(&quot;.fancybox&quot;).off()
</code></pre>
<!--kg-card-end: markdown--><h4 id="the-glue">The glue</h4><p><em>application.js</em> is now only the entry point which will listen to <em>Turbolinks</em> events to render proper views and clean them up.#= require everything you need</p><!--kg-card-begin: markdown--><pre><code class="language-javascript">#= require everything you need

pageLoad = -&gt;
  className = $(&apos;body&apos;).attr(&apos;data-class-name&apos;)
  window.applicationView = try
    eval(&quot;new #{className}()&quot;)
  catch error
    new Views.ApplicationView()
  window.applicationView.render()
  
head -&gt;
  $ -&gt;
    pageLoad()
    $(document).on &apos;page:load&apos;, pageLoad
    
    $(document).on &apos;page:before-change&apos;, -&gt;
      window.applicationView.cleanup()
      true
      
    $(document).on &apos;page:restore&apos;, -&gt;
      window.applicationView.cleanup()
      pageLoad()
      true
</code></pre>
<!--kg-card-end: markdown--><p>You also need to define a specific data attribute on each page, to indicate which JS view needs to be rendered.</p><p>In your <em>application_controller.rb</em>, define <em>js_class_name </em>method: </p><!--kg-card-begin: markdown--><pre><code class="language-ruby">def js_class_name
   action = case action_name
     when &apos;create&apos; then &apos;New&apos;
     when &apos;update&apos; then &apos;Edit&apos;
    else action_name
   end.camelize
   &quot;Views.#{self.class.name.gsub(&apos;::&apos;, &apos;.&apos;).gsub(/Controller$/, &apos;&apos;)}.#{action}View&quot;
 end
</code></pre>
<!--kg-card-end: markdown--><p>And then use it in your layout (as well as <em>HeadJS</em> initialization)</p><!--kg-card-begin: markdown--><pre><code>%html
  %head
    = javascript_include_tag &apos;head.min&apos;
    = headjs_include_tag &apos;vendor&apos;, &apos;application&apos;
  %body{&apos;data-class-name&apos; =&gt; js_class_name}
</code></pre>
<!--kg-card-end: markdown--><h3 id="final-words">Final words</h3><p>I&#x2019;m seing most and most Rails projects bootstrapped using either <em>Angular</em>, <em>Ember</em> or <em>Backbone</em> as their <em>de facto</em> choice.</p><p>Whilst these frameworks are very powerful, I hope that after reading this post you will consider that <em>not using a JS framework</em> is also a valid choice.</p>]]></content:encoded></item></channel></rss>