<?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: Riccardo Odone</title>
    <description>The latest articles on DEV Community by Riccardo Odone (@riccardoodone).</description>
    <link>https://dev.to/riccardoodone</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%2F149296%2Fac79ddc1-d1f2-457a-a9f4-84a35a42174d.jpg</url>
      <title>DEV Community: Riccardo Odone</title>
      <link>https://dev.to/riccardoodone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/riccardoodone"/>
    <language>en</language>
    <item>
      <title>How would I do it in Haskell?</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 26 Nov 2020 08:47:41 +0000</pubDate>
      <link>https://dev.to/riccardoodone/how-would-i-do-it-in-haskell-5ahc</link>
      <guid>https://dev.to/riccardoodone/how-would-i-do-it-in-haskell-5ahc</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-11-26-how-would-i-do-it-in-haskell.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;Since I started playing with functional programming, not only I've dot-chained the hell out of object-oriented code, but I often catch myself thinking: &lt;em&gt;how would I do this in Haskell?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's a couple of examples that come to mind where I contaminated Ruby with functional intuitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested Loops and Filtering
&lt;/h2&gt;

&lt;p&gt;During the &lt;a href="https://www.coderetreat.org" rel="noopener noreferrer"&gt;Global Day Of Code Retreat&lt;/a&gt;, together with &lt;a href="https://www.linkedin.com/in/jo-wojtowicz/" rel="noopener noreferrer"&gt;Joanna&lt;/a&gt;, we were writing a method to find the eight neighbors of a cell. In other words, given &lt;code&gt;[ 1, 1 ]&lt;/code&gt; as an input we wanted to generate the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;         &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We started with a simple implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# [1, 1]&lt;/span&gt;
&lt;span class="c1"&gt;# [[[0, 0], [0, 1], [0, 2]], [[1, 0], [1, 1], [1, 2]], [[2, 0], [2, 1], [2, 2]]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To remove the double nesting in the output, we reached for &lt;code&gt;flatten&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatten&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;=&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# [1, 1]&lt;/span&gt;
&lt;span class="c1"&gt;# [0, 0, 0, 1, 0, 2, 1, 0, 1, 1, 1, 2, 2, 0, 2, 1, 2, 2]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oops, that was one &lt;code&gt;flatten&lt;/code&gt; too much. We tried again with &lt;code&gt;flat_map&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;=&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# [1, 1]&lt;/span&gt;
&lt;span class="c1"&gt;# [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yay! The last piece was to remove the current cell from the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;neighbor&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;neighbor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;=&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# [1, 1]&lt;/span&gt;
&lt;span class="c1"&gt;# [[0, 0], [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1], [2, 2]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There was something inelegant about that code, though. To hell with the exercise, we wasted the rest of the session refactoring the method.&lt;/p&gt;

&lt;p&gt;The nested loop looked ugly. Not to count we were generating an &lt;em&gt;invalid&lt;/em&gt; neighbor, only to filter it out later. Here's what we came up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeated_permutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;permutation&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;permutation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# [1, 1]&lt;/span&gt;
&lt;span class="c1"&gt;# [[0, 0], [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1], [2, 2]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Semigroup in the Wild
&lt;/h2&gt;

&lt;p&gt;A few days ago, I stumbled upon the following code in a production codebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;group_by_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items_grouped_by_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;items_grouped_by_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:variation_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'quantity'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                           &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:product_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                           &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_object&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;item_grouped_by_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;items_grouped_by_template&lt;/span&gt;
  &lt;span class="vi"&gt;@order_items_grouped_by_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_object&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;item_grouped_by_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;item_grouped_by_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;variation_type: &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;template_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;variation_name: &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;product_id: &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;product_name: &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;total_quantity: &lt;/span&gt;&lt;span class="n"&gt;product_template_total_quantity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="ss"&gt;reporting_category: &lt;/span&gt;&lt;span class="n"&gt;reporting_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;item_grouped_by_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="ss"&gt;variation_type: &lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:variation_type&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="ss"&gt;product_id: &lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:product_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="ss"&gt;product_name: &lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:product_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="ss"&gt;total_quantity: &lt;/span&gt;&lt;span class="n"&gt;product_total_quantity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="ss"&gt;reporting_category: &lt;/span&gt;&lt;span class="n"&gt;order_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:reporting_category&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't understand it, don't worry, I'm there with you. I speculate the author didn't understand it, either.&lt;/p&gt;

&lt;p&gt;However, after massaging the code for a while, I noticed one thing: there's a hidden data structure that gets combined with itself: I found a &lt;a href="https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Semigroup.html" rel="noopener noreferrer"&gt;semigroup&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;Summary&lt;/code&gt; that can be &lt;code&gt;concat&lt;/code&gt;ed with another one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:variation_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:variation_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:total_quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;keyword_init: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;variation_type: &lt;/span&gt;&lt;span class="n"&gt;variation_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;variation_name: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;variation_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;variation_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;product_id: &lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;product_name: &lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;total_quantity: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:total_quantity&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in place, I could refactor to something similar to what follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;product_product_templates&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;to_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;variation_type: &lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;template_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;variation_name: &lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;product_id: &lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;product_name: &lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;total_quantity: &lt;/span&gt;&lt;span class="n"&gt;product_template_total_quantity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_product_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still ugly, but at least I can reason about it.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>functional</category>
      <category>refactorit</category>
      <category>ruby</category>
      <category>haskell</category>
    </item>
    <item>
      <title>Make Your Tests Fail Randomly (and Profit)</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 19 Nov 2020 09:23:51 +0000</pubDate>
      <link>https://dev.to/riccardoodone/make-your-tests-fail-randomly-and-profit-1g7k</link>
      <guid>https://dev.to/riccardoodone/make-your-tests-fail-randomly-and-profit-1g7k</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-11-19-make-your-tests-fail-randomly-and-profit.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;Recently, I started experimenting with random values in automated tests. It improves the readability of my code and the likelihood of failures in the presence of bugs or wrong mental models.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Story From Last Week
&lt;/h2&gt;

&lt;p&gt;I was tasked with a new feature in the middle of the worst code you can imagine. I identified a seam where I could introduce the logic, and I was glad to see the surroundings were covered with tests.&lt;/p&gt;

&lt;p&gt;However, as soon as I opened the specs, my heart missed a beat. Some tests were as dry as beef jerky:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"works for a business owner"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;perform_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@business&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other tests were soaked wet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'for a wholesaler'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'with an existing payment card'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;build_fixtures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;cart_owner: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wholesaler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:wholesaler_org&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:with_payment_card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;business: &lt;/span&gt;&lt;span class="vi"&gt;@business&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="vi"&gt;@business_cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;

    &lt;span class="n"&gt;user_session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_auth_session_for_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign_in&lt;/span&gt;

    &lt;span class="n"&gt;order_create_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;wholesaler: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;business_cart_id: &lt;/span&gt;&lt;span class="vi"&gt;@business_cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_type: &lt;/span&gt;&lt;span class="s1"&gt;'shipping'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_date: &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;week&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;iso8601&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_id: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_email: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_name: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_first_name: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_last_name: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_phone_number: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_location_type: &lt;/span&gt;&lt;span class="s1"&gt;'Address'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_location_id: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;payment_card_attributes: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;payment_type: &lt;/span&gt;&lt;span class="s1"&gt;'credit_card'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payment_cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;user_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt; &lt;span class="n"&gt;resource_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@business&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;order_create_params&lt;/span&gt;

    &lt;span class="n"&gt;expect_model_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;parent_type: &lt;/span&gt;&lt;span class="s1"&gt;'WholesalerOrg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;parent_id: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_email: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_name: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;order_customer_phone_number: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_type: &lt;/span&gt;&lt;span class="n"&gt;order_create_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:fulfillment_type&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_date: &lt;/span&gt;&lt;span class="no"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fulfillment_date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iso8601&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_location_type: &lt;/span&gt;&lt;span class="no"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;fulfillment_location_id: &lt;/span&gt;&lt;span class="no"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;total_price: &lt;/span&gt;&lt;span class="vi"&gt;@business_product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;business_product_templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unit_price&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;total_product_price: &lt;/span&gt;&lt;span class="vi"&gt;@business_product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;business_product_templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unit_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;total_tax_price: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;payment_source_id: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payment_cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect_json_sizes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'order_items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@business_cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_price&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both completely unreadable: it was not clear to me what was under test or how they even worked. So I created a brand new file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'with wholesale order it does not create tax records'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;random_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'business'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;business&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="no"&gt;Business&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;phone_number: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;wholesaler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;first_name: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;last_name: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;random_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="n"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'wholesaler'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;business_order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_business_order!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;business: &lt;/span&gt;&lt;span class="n"&gt;business&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;recipient: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;CalculateTaxesAndUpdatePrice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;business_order: &lt;/span&gt;&lt;span class="n"&gt;business_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BusinessOrderTaxSummary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BusinessOrderTaxLineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I made &lt;strong&gt;noise explicit by substituting hard-coded values with random ones&lt;/strong&gt;. The second step was to extract some builders and just leave pure signal in the spec itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'with wholesale order it does not create tax records'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;business&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_business!&lt;/span&gt;
  &lt;span class="n"&gt;wholesaler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_user!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'wholesaler'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;business_order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_business_order!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;business: &lt;/span&gt;&lt;span class="n"&gt;business&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;recipient: &lt;/span&gt;&lt;span class="n"&gt;wholesaler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;CalculateTaxesAndUpdatePrice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;business_order: &lt;/span&gt;&lt;span class="n"&gt;business_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BusinessOrderTaxSummary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;BusinessOrderTaxLineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tad more readable, huh? It's clear that &lt;code&gt;type: 'wholesaler'&lt;/code&gt; makes the difference and what the test is assessing.&lt;/p&gt;

&lt;p&gt;I made sure the suite was green and pushed to CI. A couple of minutes later, I realized the build was red: the new test failed. I tried again on my computer, green.&lt;/p&gt;

&lt;p&gt;I joked with myself that by introducing random values, I introduced random failures. Actually, that was exactly what happened, but it turned out great. Thanks to that, I discovered that &lt;em&gt;wholesale&lt;/em&gt; orders are tax-exempt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I discovered a business rule.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sure, in this case, I could have written two tests: one for retail and one for wholesale. But there's a couple of problems: I couldn't test all combinations and I would write tests according to my bias. Random values defuse both of those issues.&lt;/p&gt;

&lt;p&gt;I decided to introduce another element of randomness: instead of hardcoding one order-item per order in &lt;code&gt;create_business_order!&lt;/code&gt;, I let the code generate an array of length 0, 1, or 2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_business_order!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;business&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
  &lt;span class="no"&gt;BusinessOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;order_items: &lt;/span&gt;&lt;span class="n"&gt;many&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;build_order_item&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;many&lt;/span&gt;
  &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom: another failure. Tax calculations assumed there would always be at least one order-item and started failing with 0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I uncovered a bug in the code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To summarize, random values allowed me to upgrade my mental model and find a bug. But this is no silver bullet, at least not in this form and shape.&lt;/p&gt;

&lt;p&gt;Firstly, it's impossible to &lt;strong&gt;reproduce deterministically&lt;/strong&gt; a failing test run: random values change every time.&lt;/p&gt;

&lt;p&gt;This can be solved easily, at least in Ruby. The &lt;code&gt;rspec&lt;/code&gt; test runner uses a seed value to randomly generate the order to run specs. The following line primes the Ruby's pseudo-random number generator with the same value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;srand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in place, executing &lt;code&gt;rspec --seed 1234&lt;/code&gt; twice will both run tests in the same order and generate the same "random" values.&lt;/p&gt;

&lt;p&gt;Secondly, it would be best if a &lt;strong&gt;test either always passed or not&lt;/strong&gt;. With random values, it's not possible to guarantee that level of determinism. But running the tests multiple times gets it close enough. Remember, even when the test is run only once, it still covers more than a test with hardcoded values; it just takes more time.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;rspec&lt;/code&gt; it could be achieved with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"tests something with random values"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This is an excellent idea; you are a genius!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wish I could take credit for it. I stole this technique from Romeu Moura after attending his &lt;a href="https://www.youtube.com/watch?v=pX44CoRSIpg" rel="noopener noreferrer"&gt;Domain Invariants &amp;amp; Property-Based Testing for the Masses&lt;/a&gt;. And I'm guilty as charged since I pop up in the recording several times. I had short hair and wasn't as pink at the time, so bonus points if you can spot me.&lt;/p&gt;

&lt;p&gt;Should you wish to explore this topic more in-depth, I suggest you look into Property-Based Testing. I also wrote &lt;a href="https://medium.com/hackernoon/property-based-testing-4330e3e77381" rel="noopener noreferrer"&gt;Property-based testing (with a sprinkle of JavaScript)&lt;/a&gt; and &lt;a href="https://medium.com/hackernoon/diamond-kata-via-property-based-tdd-in-javascript-5fa99acd3e62" rel="noopener noreferrer"&gt;Diamond kata via property-based TDD in JavaScript&lt;/a&gt;. Both featured on HackerNoon, so you bet they are good reads!&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>legacycode</category>
      <category>essentialskills</category>
    </item>
    <item>
      <title>How to Terminate Legacy Code without Getting Stuck</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Wed, 11 Nov 2020 08:14:31 +0000</pubDate>
      <link>https://dev.to/riccardoodone/how-to-terminate-legacy-code-without-getting-stuck-1c80</link>
      <guid>https://dev.to/riccardoodone/how-to-terminate-legacy-code-without-getting-stuck-1c80</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-11-11-how-to-terminate-legacy-code-without-getting-stuck.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;Legacy and greenfield projects are two different beasts. Both require coding, but different rules apply. Failing to make the mental switch is an excellent recipe for getting stuck.&lt;/p&gt;

&lt;p&gt;Some developers are perfectionists. But all of us have a minimum level of decency. We would never push bad code according to our definition of quality and skills.&lt;/p&gt;

&lt;p&gt;Having an eye for good code is great in greenfield situations where the lack of constraints allows crafting software with finesse.&lt;/p&gt;

&lt;p&gt;However, legacy code sits way below our minimum decency level and would require too much work to rewrite. &lt;strong&gt;We cannot undo months of accrued technical debt in a few days&lt;/strong&gt;. And we shouldn't even try.&lt;/p&gt;

&lt;p&gt;In this situation, we should embrace the &lt;em&gt;inversion principle&lt;/em&gt;. Instead of trying to reach success, we should strive to avoid failure. Rather than repaying all the technical debt, we should &lt;strong&gt;just make it less awful&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Legacy code is not created all the same. Some never change and should be left alone. Some require multiple interventions. Don't try to make it perfect; just make it less awful for the next round of changes.&lt;/p&gt;

&lt;p&gt;You will be back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwksjdcgqdb3kx5l53qn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwksjdcgqdb3kx5l53qn.gif" alt="I'll be back" width="500" height="267"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>learning</category>
      <category>legacycode</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Vanilla JavaScript vs. RxJs</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 05 Nov 2020 09:23:52 +0000</pubDate>
      <link>https://dev.to/riccardoodone/vanilla-javascript-vs-rxjs-337a</link>
      <guid>https://dev.to/riccardoodone/vanilla-javascript-vs-rxjs-337a</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-11-05-vanilla-javascript-vs-rxjs.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;This post compares vanilla JavaScript with RxJs. My intent is not to demonstrate whether one or the other is the best approach. As always, it depends.&lt;/p&gt;

&lt;p&gt;But I want to highlight the importance of &lt;strong&gt;tackling a problem from different angles&lt;/strong&gt;. In this case, it's imperative against declarative, or "push" vs. "pull."&lt;/p&gt;

&lt;p&gt;Also, different mental models provide insights that can be exploited in the solution, regardless of the paradigm chosen. In this article, the imperative approach helps exploring the problem, the declarative one distills the solution: both have their merits.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's Monday Morning
&lt;/h2&gt;

&lt;p&gt;While you wait for the browser to load the to-dos, you wonder about the feature you will be working on today.&lt;/p&gt;

&lt;p&gt;Maybe you will work in Elm-land where run-time exceptions never show up, or you will be modeling new domains in Haskell where impossible states don't compile.&lt;/p&gt;

&lt;p&gt;Nope, it's JavaScript. You need to add an input field to enable users to fetch data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Damn&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You believe in small steps and short feedback loops, so this is your first move:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;queryElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A glance at the browser confirms that typing in the input field logs the value in the console. Great!&lt;/p&gt;

&lt;p&gt;Time to fetch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-const callback = value =&amp;gt; console.log(value)
&lt;/span&gt;&lt;span class="gi"&gt;+const callback = query =&amp;gt;
+  fetch(`https://httpbin.org/get?query=${encodeURIComponent(query)}`)
+    .then(response =&amp;gt; response.json())
+    .then(response =&amp;gt; console.log(response))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another quick manual test confirms that the requests work.&lt;/p&gt;

&lt;p&gt;You spend the rest of the day making things pretty and replacing the &lt;code&gt;console.log()&lt;/code&gt; with the appropriate function to fill the DOM. Then, you move the ticket to done full of pride.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That was slick!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, the next day you get an email from the devops team with the following subject: &lt;strong&gt;URGENT!1!&lt;/strong&gt;. After your deploy, servers started receiving a ton of requests.&lt;/p&gt;

&lt;p&gt;You open the application and type "holy moly!" in the text field. Your heart skips a bit when you notice it generated 10 network requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"h"&lt;/li&gt;
&lt;li&gt;"ho"&lt;/li&gt;
&lt;li&gt;"hol"&lt;/li&gt;
&lt;li&gt;"holy"&lt;/li&gt;
&lt;li&gt;"holy "&lt;/li&gt;
&lt;li&gt;"holy m"&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Holy moly! indeed, I forgot to debounce!&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+const DEBOUNCE_MILLISECONDS = 300
+let scheduled
+
&lt;/span&gt; const callback = query =&amp;gt;
   fetch(`https://httpbin.org/get?query=${encodeURIComponent(query)}`)
     .then(response =&amp;gt; response.json())
     .then(response =&amp;gt; console.log(response))
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+const debounce = fnc =&amp;gt; arg =&amp;gt; {
+  clearTimeout(scheduled)
+  scheduled = setTimeout(() =&amp;gt; fnc(arg), DEBOUNCE_MILLISECONDS)
+}
+
+const debouncedCallback = debounce(callback)
+
&lt;/span&gt; const queryElement = document.getElementById("query")
&lt;span class="gd"&gt;-queryElement.addEventListener('input', event =&amp;gt; callback(event.target.value))
&lt;/span&gt;&lt;span class="gi"&gt;+queryElement.addEventListener('input', event =&amp;gt; debouncedCallback(event.target.value))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure not to piss the ops team again, you get deeper into manual testing. The debouncing works, but there is something strange: sometimes, the application displays data for an old query.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aha, the responses are coming out of order.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To make it more visible you introduce a random delay in the &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+const throttledFetch = (url, options) =&amp;gt; {
+  return new Promise((res, rej) =&amp;gt; {
+    const throttleBy = Math.random() * 10000
+    console.log(`throttledBy ${throttleBy} milliseconds`)
+    fetch(url)
+      .then(x =&amp;gt; setTimeout(() =&amp;gt; res(x), throttleBy))
+      .catch(x =&amp;gt; setTimeout(() =&amp;gt; rej(x), throttleBy))
+  })
+}
+
&lt;/span&gt; const callback = query =&amp;gt;
&lt;span class="gd"&gt;-  fetch(`https://httpbin.org/get?query=${encodeURIComponent(query)}`)
&lt;/span&gt;&lt;span class="gi"&gt;+  throttledFetch(`https://httpbin.org/get?query=${encodeURIComponent(query)}`)
&lt;/span&gt;     .then(response =&amp;gt; response.json())
     .then(response =&amp;gt; console.log(response))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luckily, you can abort the previous &lt;code&gt;fetch&lt;/code&gt; before executing the next one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+let controller = new AbortController()
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; const throttledFetch = (url, options) =&amp;gt; {
   return new Promise((res, rej) =&amp;gt; {
     const throttleBy = Math.random() * 10000
     console.log(`throttleBy ${throttleBy} milliseconds`)
&lt;span class="gd"&gt;-    fetch(url)
&lt;/span&gt;&lt;span class="gi"&gt;+    controller.abort()
+    controller = new AbortController()
+    fetch(url, { signal: controller.signal })
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's almost the end of the day, and you are staring at this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEBOUNCE_MILLISECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;scheduled&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;throttledFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rej&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;throttleBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`throttleBy &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;throttleBy&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; milliseconds`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;throttleBy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;rej&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;throttleBy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;throttledFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://httpbin.org/get?query=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fnc&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheduled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;scheduled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fnc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;DEBOUNCE_MILLISECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;queryElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debouncedCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The throttling code needs to be removed. Still, the software crafter inside your head is in pain. You shouldn't have to tell JavaScript what to do line by line.&lt;/p&gt;

&lt;p&gt;Instead of "pushing" information around, you want to "pull" and react to it. It should be as declarative as a spreadsheet.&lt;/p&gt;

&lt;p&gt;It's too late to conjure that thought, your fingers are already typing &lt;code&gt;yarn add rxjs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;fromEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fromFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://httpbin.org/get?query=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only this achieves the same result, but also it's shorter and declarative. Not to count the additional insight you notice from this new angle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const queryElement = document.getElementById("query")
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;fromEvent(queryElement, 'input').pipe(
&lt;/span&gt;  debounceTime(300),
  map(event =&amp;gt; event.target.value),
&lt;span class="gi"&gt;+ distinctUntilChanged(),
&lt;/span&gt;  switchMap(query =&amp;gt; fromFetch(`https://httpbin.org/get?query=${encodeURIComponent(query)}`)),
  flatMap(response =&amp;gt; response.json()),
  catchError(error =&amp;gt; console.log(error))
)
.subscribe(response =&amp;gt; console.log(response))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You make sure nobody else is looking, you sneak in the additional dependency, and you deploy.&lt;/p&gt;

&lt;p&gt;Now, it's the end of the day!&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>rxjs</category>
      <category>javascript</category>
      <category>learning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Simplest Most Powerful Trick–Verification Steps</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 29 Oct 2020 09:26:19 +0000</pubDate>
      <link>https://dev.to/riccardoodone/the-simplest-most-powerful-trick-verification-steps-3jnf</link>
      <guid>https://dev.to/riccardoodone/the-simplest-most-powerful-trick-verification-steps-3jnf</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-10-29-the-simplest-most-powerful-trick-verification-steps.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;Manual testing is an essential piece in the software development puzzle. Still, it's often not given the respect it deserves. Sometimes, teams work without any quality assurance and don't test at all. Sometimes, it's the author of the feature who half-asses manual testing. Sometimes, it's the client that clicks around the interface without any guidance.&lt;/p&gt;

&lt;p&gt;Sure, it would be best to have an expert tester on the team. Still, not having one should not mean disregarding manual testing. If you are reading this article, I bet you are not a chef, but you manage to eat every day. Instead, we should enable everybody to perform manual testing. Even if the team can count on a dedicated tester.&lt;/p&gt;

&lt;p&gt;Some years ago, I was exposed to an equally simple and powerful idea: &lt;strong&gt;verification steps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqn3d14sq4ri1wou7asds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqn3d14sq4ri1wou7asds.png" alt="Screenshot of verification steps out of a Trello ticket. Verification steps: 1. login on staging with riccardo.odone 2. open reporting 3. go to production checklist 4. notice it's fast enough 5. select dates to cover September (14th has 25 orders, 15th has 50 orders, etc.) 6. notice the pagination (50 per page) 7. wait a few seconds 8. notice the print button becomes active 9. notice that printing would print all pages not only the current one 10. notice, both in the page and in printing, items are ordered by fulfillment date first, order number second (Please check with other accounts too, Please do a light testing of manage orders and tax summary–just visualizing them not generating)" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verification steps provide guidance on &lt;strong&gt;how to verify a feature implementation&lt;/strong&gt;. They look similar to a description of how to reproduce an issue in a bug report. They don't come with a 100% save-ass guarantee, but the list of benefits is long, and it's not limited to finding bugs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;. Ticket descriptions tend to be in the form of a conversation, not to count all the back-and-forth in the comments. Verification steps work as a TL;DR.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;BDD&lt;/strong&gt;. As a developer, I love diving into code right away. Sometimes it means I get lost in the details and lose sight of the big picture. Having a clear step-by-step breakdown in the ticket helps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Definition of done&lt;/strong&gt;. In some cases, it's not clear when to stop working on a feature. By following the steps, it's easy to verify that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Empathy&lt;/strong&gt;. Verification steps are a journey, the same one users will embark on. By following the same path, it's easier to get in their shoes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep the team in sync&lt;/strong&gt;. When everybody is working on their stuff, silos are born. By encouraging the team to test tickets via the verification steps, knowledge spreads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ensure successful deploys&lt;/strong&gt;. Ever &lt;code&gt;git push&lt;/code&gt;ed and realized, hours later, it has been rejected because your local branch was behind? Or maybe forgot to toggle a feature flag? I cannot count the number of times I deployed a feature when I actually hadn't. Luckily, anybody can catch it by following the verification steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build things right&lt;/strong&gt;. When focused on coding, developers embed their own bias into the software. A list of steps can not only shake programmers out of their own bubble, but they also allow the verifier to test according to their own bias. And yes, the more diverse the team, the better.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build the right things&lt;/strong&gt;. It's challenging to &lt;em&gt;feel&lt;/em&gt; a feature by reading a ticket description. Verification steps make it more apparent what the final user experience would be. Also, designers can make sure the steps represent what they envisioned.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Doing one thing at a time&lt;/strong&gt;. Any writer would say that drafting and editing don't go hand-in-hand. The same is valid for coding and manual testing. When the author of the code and the tester are the same person, verification steps help to enforce the boundaries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Serendipitous verification&lt;/strong&gt;. I had clients, designers, and testers providing better technical solutions than whatever I came up with in the past. By enabling everybody to chime in with their expertise, bias, and freshness, better solutions surface.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>testing</category>
      <category>teamwork</category>
      <category>essentialskills</category>
    </item>
    <item>
      <title>How to Deal with (Your) Unsatisfying Code</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 22 Oct 2020 07:50:57 +0000</pubDate>
      <link>https://dev.to/riccardoodone/how-to-deal-with-your-unsatisfying-code-3pa7</link>
      <guid>https://dev.to/riccardoodone/how-to-deal-with-your-unsatisfying-code-3pa7</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-10-22-how-to-deal-with-your-unsatisfying-code.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;Shit happens.&lt;/p&gt;

&lt;p&gt;From time to time, I craft software that is not satisfying. Maybe it's messy, verbose, or namings are off. Maybe it's over-tested or under-tested. Maybe it's not correct, or maybe it's over-engineered. Maybe it just sucks, but I don't know why.&lt;/p&gt;

&lt;p&gt;This happens when there's a gap between what I developed and what perfect could look like. It's also known as technical debt. The more of it, the more time it will take to understand the code in the future.&lt;/p&gt;

&lt;p&gt;In the past, I committed the crime of adding a &lt;em&gt;Refactor Feature X&lt;/em&gt; ticket to the cemetery (aka backlog). In other words, I handed off the problem to somebody else in the future (sometimes myself). It didn't work well, but, luckily, there are better ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Debt is Not All the Same
&lt;/h2&gt;

&lt;p&gt;Let's say there's a box somewhere stuffed with spaghetti code. It works correctly and never requires changes. Should you rush to refactor it? Of course not; it's a debt that doesn't need to be repaid.&lt;/p&gt;

&lt;p&gt;However, with bugs or spec changes, you have to open that box and understand what's going on.&lt;/p&gt;

&lt;p&gt;If you had a choice, which one of the following parallel universes would you choose?&lt;/p&gt;

&lt;p&gt;Parallel universe number one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeClass&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="c1"&gt;# 300 lines of code&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parallel universe number two:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeClass&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;go_for_it&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FetchDataFromDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;go_for_it&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PerformSomeCalculations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;go_for_it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;SaveDataToDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;go_for_it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FetchDataFromDatabase&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="c1"&gt;# 100 lines of code&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PerformSomeCalculations&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="c1"&gt;# 100 lines of code&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SaveDataToDatabase&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="c1"&gt;# 100 lines of code&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would pick number two.&lt;/p&gt;

&lt;p&gt;The chaos that can be fit in a box is proportional to its volume: three smaller boxes are better than a big one. Most importantly, in the second universe, &lt;code&gt;SomeClass&lt;/code&gt; works as an index. It gives a high-level overview of the algorithm. Also, it suggests what boxes to open to change a specific aspect of the feature. And there's a chance it's only one class, not all of them.&lt;/p&gt;

&lt;p&gt;If you lost the keys to your car, would you rather know they are somewhere in the apartment or in one of the drawers close to the entrance?&lt;/p&gt;

&lt;h2&gt;
  
  
  So What to Do with Unsatisfying Code?
&lt;/h2&gt;

&lt;p&gt;If the specifications are stable and the code works, better to leave it alone.&lt;/p&gt;

&lt;p&gt;If it's likely to change, try to make sure it works correctly, and it reveals intentions. In other words, on the one hand, make a change less likely, and, on the other hand, make sure the future reader will be guided throughout the code.&lt;/p&gt;

&lt;p&gt;You can structure your process to (almost) guarantee code that is both correct and intention revealing. Should you accept the challenge, here's some food for thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://odone.me/posts/2020-06-29-decomposing-features-into-pipelines.html" rel="noopener noreferrer"&gt;Decomposing Features into Pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://odone.me/posts/2020-08-28-how-to-tame-complexity-into-simplicity-with-a-shake-list.html" rel="noopener noreferrer"&gt;How to Tame Complexity Into Simplicity with a Shake List&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://odone.me/posts/2020-08-20-code-quality-is-free-if-you-do-it-right.html" rel="noopener noreferrer"&gt;Code Quality is Free (if You Do it Right)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://odone.me/posts/2020-07-24-naming-things-made-easy.html" rel="noopener noreferrer"&gt;Naming Things Made Easy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://odone.me/posts/2020-06-19-starting-from-the-problem-not-the-solution.html" rel="noopener noreferrer"&gt;Starting from the Problem not the Solution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>learning</category>
      <category>codequality</category>
      <category>technicaldebt</category>
    </item>
    <item>
      <title>From Zero to RxJs via Knowledge Transfer</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 15 Oct 2020 07:12:42 +0000</pubDate>
      <link>https://dev.to/riccardoodone/from-zero-to-rxjs-via-knowledge-transfer-3g5f</link>
      <guid>https://dev.to/riccardoodone/from-zero-to-rxjs-via-knowledge-transfer-3g5f</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-10-15-from-zero-to-rxjs-via-knowledge-transfer.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;A couple of weeks ago, I talked about going &lt;a href="https://odone.me/posts/2020-10-01-from-temporary-knowledge-to-permanent-knowledge.html" rel="noopener noreferrer"&gt;From Temporary Knowledge to Permanent Knowledge&lt;/a&gt;. Last week, I shared &lt;a href="https://odone.me/posts/2020-10-09-10-knowledge-transfers-that-make-me-thrive-with-legacy-code.html" rel="noopener noreferrer"&gt;10 Knowledge Transfers that Make me Thrive with Legacy Code&lt;/a&gt;. Today, I want to ride the momentum and focus on how permanent knowledge got me up to speed with RxJs.&lt;/p&gt;

&lt;p&gt;RxJs is a complex library. It's confusing because it requires solving problems differently. In particular, it makes heavy use of four concepts that are not straightforward by themselves: async programming, streams, event-driven modeling, and functional programming.&lt;/p&gt;

&lt;p&gt;RxJs is incredibly powerful and declarative, though. Let me shamefully steal some code from &lt;a href="https://rxjs.dev/guide/overview" rel="noopener noreferrer"&gt;rxjs.dev&lt;/a&gt; to show why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Vanilla JavaScript&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastClick&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;lastClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// RxJs&lt;/span&gt;

&lt;span class="nf"&gt;fromEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the cost of increased complexity, RxJs subscribes to a stream of click events, employing local state, and composing declarative code with functional programming trickery. Said differently, the vanilla JavaScript code models the evolution of a value over time by mutating (global) state.&lt;/p&gt;

&lt;p&gt;I would posit, complexity is there regardless; in one case, it's in using RxJs; in the other, it's embedded in the vanilla JavaScript code. But I digress.&lt;/p&gt;

&lt;p&gt;Hopefully, the example justifies the need for a different approach when writing RxJs code. However, I quickly managed to get up to speed by transferring knowledge from the past and filling in the blanks.&lt;/p&gt;

&lt;p&gt;It's similar to those RPG games where the map is black until the area is explored. I had already visited most of the places surrounding the principles RxJs builds upon. These mental models allowed me to approach the library with ease. Let's see how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streams
&lt;/h2&gt;

&lt;p&gt;In RxJs, everything is a value that evolves over time. It's like an array of elements that are appended eventually. That is why, in the example above, it's possible to &lt;code&gt;map&lt;/code&gt; to transform each value, including the future ones!&lt;/p&gt;

&lt;p&gt;In other words, arrays support a pull use-case while streams a push use-case. If we needed to see the room temperature in real-time, the former would require polling and state mutations; the latter would just work.&lt;/p&gt;

&lt;p&gt;Great, modulo some quirks streams can be treated like arrays.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async Programming
&lt;/h2&gt;

&lt;p&gt;I still remember the confusion when I started working with promises (or futures). I could not reconcile sync and async programming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// WRONG: it logs the promise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I realized when you go async, you cannot go back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(...).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a promise produces a value eventually, streams produce multiple values eventually. But everything is async, so the former requires &lt;code&gt;then&lt;/code&gt;, the latter provides several operators (see the initial example).&lt;/p&gt;

&lt;p&gt;Awesome, streams are like promises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-Driven Modelling
&lt;/h2&gt;

&lt;p&gt;Visualizing a problem in terms of events is strange. Luckily, I was exposed to DDD and Event Storming early in my career. However, we all know a perfect example of event-driven programming: spreadsheets.&lt;/p&gt;

&lt;p&gt;In particular, to calculate in C1 the addition of A1 and A2, it's enough to use &lt;code&gt;=A1+A2&lt;/code&gt;. That is, whenever A1 or A2 changes, update the cell to their sum. Notice that nobody "pushes" values to C1; it's C1 that "pulls" from A1 and A2. I find it elegant and clean.&lt;/p&gt;

&lt;p&gt;Nice, event-driven programming is like using spreadsheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functional Programming
&lt;/h2&gt;

&lt;p&gt;RxJs employs functional programming for flow control, composition, and decomposition. On top of that, it adheres to many interfaces (typeclasses) that make me feel at home.&lt;/p&gt;

&lt;p&gt;By looking at the &lt;a href="https://pursuit.purescript.org/packages/purescript-rx/2.0.0/docs/Rx.Observable" rel="noopener noreferrer"&gt;PureScript bindings for RxJs&lt;/a&gt;, it's clear that an Observable is a Functor, a Monad, a Semigroup, and many others. That is enough to know I can use &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;flatMap&lt;/code&gt;, &lt;code&gt;concat&lt;/code&gt;, and more.&lt;/p&gt;

&lt;p&gt;Hell yeah, learning monads was not time wasted!&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;Hopefully, this explains how knowledge transfer allowed me to rock and roll with RxJs.&lt;/p&gt;

&lt;p&gt;Notice that permanent knowledge compounds. When I'm exposed to a new concept close to a dense area of my knowledge graph, I can approach it from multiple angles making sense of it with different mental models.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>10 Knowledge Transfers that Make me Thrive with Legacy Code</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Fri, 09 Oct 2020 11:33:26 +0000</pubDate>
      <link>https://dev.to/riccardoodone/10-knowledge-transfers-that-make-me-thrive-with-legacy-code-5gfg</link>
      <guid>https://dev.to/riccardoodone/10-knowledge-transfers-that-make-me-thrive-with-legacy-code-5gfg</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-10-09-10-knowledge-transfers-that-make-me-thrive-with-legacy-code.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;It's a sunny day, like many others. You just got to the trash room to throw away the rubbish. Something strange jumps to your eyes as soon as you open one of the bins. It's a metal box. Lifting it up, you notice it ticks the seconds, and it displays a countdown on a red LED screen. Currently, it's marking 01:14 and going down. A jolt of adrenaline starts circulating in your blood: &lt;strong&gt;it's a bomb&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You are in luck; it's the perfect day to find explosives. Yesterday, you spent the entire evening on a Mission Impossible marathon. You manage to calm down and recall what needs to be done: &lt;strong&gt;cut the blue wire&lt;/strong&gt;. You lift the box with a smooth movement, lay it down, and open the lid with care.&lt;/p&gt;

&lt;p&gt;Cut the blue wire. Cut the blue wire. Cut the blue wire. To your dismay, as soon as the circuit is exposed, you notice wires of all colors but blue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legacy software is that bomb&lt;/strong&gt;. Every time, it's different in its own dramatic way: there are the challenges of greenfield code and all the constraints coming from choices made in the past. As they say, it's easy to perform well when things are predictable and straightforward. Unfortunately, you don’t know what to expect when dealing with legacy code until you open the ticking box.&lt;/p&gt;

&lt;p&gt;I'm &lt;a href="https://odone.me/posts/2020-07-10-grateful-for-the-opportunity-of-working-on-legacy-code.html" rel="noopener noreferrer"&gt;currently rescuing such a codebase&lt;/a&gt;. It's not so awful because I've seen more spaghetti in my life. Trust me, I'm Italian. However, it's problematic enough to have discouraged developers in the past.&lt;/p&gt;

&lt;p&gt;After several weeks, I'm proud to say that not only I overcame the mess, but also I've been thriving in this project. I credit investing in transforming &lt;a href="https://odone.me/posts/2020-10-01-from-temporary-knowledge-to-permanent-knowledge.html" rel="noopener noreferrer"&gt;temporary knowledge into permanent knowledge&lt;/a&gt;. In other words, I didn't have to relearn anything from scratch; I only transferred knowledge over from past experiences and filled the blanks.&lt;/p&gt;

&lt;p&gt;Here are the ten knowledge transfers that saved my ass:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Redux
&lt;/h2&gt;

&lt;p&gt;In the past, I've dealt with several React applications that made use of Redux to manage state. But I didn't stop there. I decided to &lt;a href="https://medium.com/hackernoon/selectors-in-redux-are-a-must-d6b0637c79b7" rel="noopener noreferrer"&gt;learn Redux's principles&lt;/a&gt; instead of just learning how to use it in a specific context. In other words, I laddered up from API bindings to architecture.&lt;/p&gt;

&lt;p&gt;The project I'm working on employs NgRx, which, in turn, relies on Redux. I didn't have to start over with reducers, selectors, and so on. I took a quick glance at the NgRx's docs, and I was good to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Confidence
&lt;/h2&gt;

&lt;p&gt;Legacy code is an emotional rollercoaster. Every up is matched by a down. Some days you think you've killed it just to realize later you screwed it up.&lt;/p&gt;

&lt;p&gt;In the past, I had several experiences like that. This is why I'm confident that, as long as there is a roadmap, things will turn out ok; it's just a matter of time. Hell, it &lt;a href="https://odone.me/posts/2020-08-10-elm-tricks-from-production%E2%80%93from-angular-v1-to-elm-in-4-days.html" rel="noopener noreferrer"&gt;took us two years to remove Angularjs from AirCasting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another superpower I embrace is being at ease with ignorance. There is no way to know each and every corner of a codebase. It's quite dangerous to delude oneself of the contrary. I'm confident in being ignorant because it &lt;a href="https://odone.me/posts/2020-07-03-the-secret-to-getting-unstuck-when-investigating-a-bug.html" rel="noopener noreferrer"&gt;actually makes a difference&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Refactoring and Code Design
&lt;/h2&gt;

&lt;p&gt;The constraints of a legacy codebase require expertise and flexibility. The strategies and tactics you apply by the book in a greenfield situation won't work. And thinking otherwise will result in &lt;a href="https://odone.me/posts/2020-08-07-the-three-step-recipe-to-success-with-legacy-code-without-getting-overwhelmed.html" rel="noopener noreferrer"&gt;being overwhelmed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most of the time, clean red, green, refactor cycles won't be an option. Also, you won't get to the perfect architecture on the first attempt. Legacy code grows messy over a long period, we should not expect to fix it in one take.&lt;/p&gt;

&lt;p&gt;Nowadays, I learned to obtain feedback, not necessarily via TDD, to refactor with confidence. At the same time, I developed an eye for design (object-oriented and functional). Without it, the only refactoring one can afford is small in scope, like renaming a method or extracting a function.&lt;/p&gt;

&lt;p&gt;I believe that &lt;a href="https://odone.me/posts/2020-08-20-code-quality-is-free-if-you-do-it-right.html" rel="noopener noreferrer"&gt;code quality is free when done right&lt;/a&gt;, and in &lt;a href="https://odone.me/posts/2020-08-28-how-to-tame-complexity-into-simplicity-with-a-shake-list.html" rel="noopener noreferrer"&gt;coding simple solutions to complex problems by design&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Being a Team Player
&lt;/h2&gt;

&lt;p&gt;I'm astonished by the miracle that a team worked when I look at delightful software. There are so many things that could go wrong. The most challenging part is the coordination, though. Designers and developers need to understand each other. The team needs to foster feedback from the customers and analyze all the nuances. Everybody needs to agree on tabs vs. spaces.&lt;/p&gt;

&lt;p&gt;I'm a fan of &lt;a href="https://odone.me/posts/2020-08-14-how-to-conjure-your-team-magic-with-a-few-stickies-and-the-playbook-exercise.html" rel="noopener noreferrer"&gt;creating a team playbook&lt;/a&gt;, writing &lt;a href="https://odone.me/posts/2020-05-15-living-together-team.html" rel="noopener noreferrer"&gt;livable code&lt;/a&gt;, and focusing on &lt;a href="https://odone.me/posts/2020-05-08-on-productivity.html" rel="noopener noreferrer"&gt;generativity (rather than productivity)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Having a Product Mindset
&lt;/h2&gt;

&lt;p&gt;A delightful product is not born by &lt;a href="https://odone.me/posts/2020-09-10-99-percent-done.html" rel="noopener noreferrer"&gt;just moving tickets to done&lt;/a&gt;, &lt;a href="https://odone.me/posts/2020-04-23-learning-commercial-projects.html" rel="noopener noreferrer"&gt;deploying the latest bleeding-edge technology&lt;/a&gt;, or &lt;a href="https://odone.me/posts/2020-06-19-starting-from-the-problem-not-the-solution.html" rel="noopener noreferrer"&gt;solving the wrong problems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Everything is a hypothesis until it's validated. We need to &lt;a href="https://odone.me/posts/2020-06-05-asking-why-to-uncover-assumptions.html" rel="noopener noreferrer"&gt;uncover assumptions&lt;/a&gt;, &lt;a href="https://odone.me/posts/2020-05-29-silly-questions.html" rel="noopener noreferrer"&gt;ask silly questions&lt;/a&gt;, and &lt;a href="https://odone.me/posts/2020-06-12-measuring-disagreement-with-standard-deviation.html" rel="noopener noreferrer"&gt;find and treasure the most controversial disagreements&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Angular v4
&lt;/h2&gt;

&lt;p&gt;This project could have been built in Rails. Unfortunately, it was overengineered. Every time I'm changing a form, I cannot help but think I'm doing double the work, with double the risks, without any gain.&lt;/p&gt;

&lt;p&gt;My only experience with Angular has been with &lt;a href="https://odone.me/posts/2020-07-06-elm-tricks-from-production%E2%80%93intro.html" rel="noopener noreferrer"&gt;Angularjs in AirCasting&lt;/a&gt;. However, combined with several other projects on the frontend, it jumpstarted my understanding of Angular v4. Two-way data bindings, components' lifecycle, automatic dependency injection, routing, among other things, are common concepts across the board.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. RxJs
&lt;/h2&gt;

&lt;p&gt;Angular v4 makes extensive use of RxJs. Here, I found myself at home, thanks to my experience with Haskell and PureScript. As soon as I understood I was dealing with composition, functors, monads, and the like, I just did the functional programming thing: &lt;a href="https://odone.me/posts/2020-06-29-decomposing-features-into-pipelines.html" rel="noopener noreferrer"&gt;decomposed to simple problems and recomposed with the correct operators&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. TypeScript
&lt;/h2&gt;

&lt;p&gt;In Angular v4, types are a thing. To be honest, it was a let down to discover that, by default, &lt;code&gt;any&lt;/code&gt; and &lt;code&gt;null&lt;/code&gt; are allowed in TypeScript. The project I'm working on has 546 occurrences of &lt;code&gt;any&lt;/code&gt;s, and makes extensive use of &lt;code&gt;null&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;It's a frustrating experience coming from Elm where runtime exceptions don't exist. Not all is lost, though; many of the techniques I learned in Elm to &lt;a href="https://odone.me/posts/2020-07-20-elm-tricks-from-production%E2%80%93declarative-bug-free-user-interfaces-with-custom-types.html" rel="noopener noreferrer"&gt;make programs correct by design&lt;/a&gt; can still be employed.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Tweaking Performance
&lt;/h2&gt;

&lt;p&gt;Rescuing the codebase, we realized it's slow and breaks when there is too much data to send to the frontend. Who would have guessed pagination in a CRUD application was a requirement?!&lt;/p&gt;

&lt;p&gt;Performance work is not my best suit, but permanent knowledge helped. I haven't stopped at optimizing Ruby and Rails code in the past. I learned to &lt;a href="https://odone.me/posts/2020-09-16-how-to-investigate-performance-issues-in-a-web-app-with-a-simple-script.html" rel="noopener noreferrer"&gt;make things reproducible&lt;/a&gt;, measure them, and &lt;a href="https://odone.me/posts/2020-09-23-making-an-endpoint-13-times-faster.html" rel="noopener noreferrer"&gt;tweak the most significant bottlenecks first&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Solving Problems
&lt;/h2&gt;

&lt;p&gt;As knowledge workers, we should go all-in to cultivate the ability to perform cognitively demanding work without interruptions. In a disrupted world, it's becoming rarer and rarer to find people who can sustain the discomfort of long and deep sessions of work.&lt;/p&gt;

&lt;p&gt;Just yesterday, I opened a file, looked at it from multiple perspectives, and stared some more to understand how to tackle it. Had I given in to distractions, it would have taken more time, and the solution would have been inferior. Not to count the frustration of reloading information in my mental RAM at each context switch.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>career</category>
      <category>learning</category>
      <category>productivity</category>
      <category>legacy</category>
    </item>
    <item>
      <title>From Temporary Knowledge to Permanent Knowledge</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 01 Oct 2020 08:31:34 +0000</pubDate>
      <link>https://dev.to/riccardoodone/from-temporary-knowledge-to-permanent-knowledge-6dj</link>
      <guid>https://dev.to/riccardoodone/from-temporary-knowledge-to-permanent-knowledge-6dj</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-10-01-from-temporary-knowledge-to-permanent-knowledge.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;My professional career started under the wings of two exceptional people: &lt;a href="https://twitter.com/szynszyliszys" rel="noopener noreferrer"&gt;Ania&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/marcinkostrzewa" rel="noopener noreferrer"&gt;Marcin&lt;/a&gt;. They taught me something that stayed with me ever since.&lt;/p&gt;

&lt;p&gt;I picture three elements of doing something: the WHY, the HOW, and the WHAT. We start with a goal, we rely on strategies and tactics, and execute.&lt;/p&gt;

&lt;p&gt;During my internship, my mentors kept insisting on the WHY. I'm glad they entrusted me with figuring out the HOW and the WHAT. Because it's the WHY where I needed guidance.&lt;/p&gt;

&lt;p&gt;They gifted me with an unfair advantage: extrapolating permanent knowledge from temporary knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Temporary Knowledge
&lt;/h2&gt;

&lt;p&gt;Temporary knowledge expires. There's a long list of frameworks and technologies that were popular just a few years ago and are already long forgotten.&lt;/p&gt;

&lt;p&gt;Recently, I've read the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Because of the temporary nature of the knowledge capital, computer programmers quickly reach a stage in their career when their old knowledge capital becomes worthless at the same rate as they acquire new knowledge capital. Their total knowledge capital is no longer increasing, so neither does their salary increase. They have reached the dead end plateau of their career, and it happens after less than ten years in the field.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To which somebody replied with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One difference I've seen from young developers and more mature developers is that more mature developers tend to have a better understanding of how users will operate and will help mitigate issues before they arise. They have wisdom and that certainly isn't "temporary knowledge capital".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a developer, I wish the latter was true. However, I cannot help but think there's some merit to the first take. I posit both opinions are valid through the proper lens, and we better be aware of it. Let me explain.&lt;/p&gt;

&lt;p&gt;The WHATs of anything are bound to its context and lifecycle. For example, the &lt;a href="https://odone.me/posts/2020-06-01-records-haskell.html" rel="noopener noreferrer"&gt;syntax for records in Haskell&lt;/a&gt; is relevant only to Haskell code. However, the HOWs and the WHYs are part of the nature of software to a more significant extent. They can be transferred to other languages.&lt;/p&gt;

&lt;p&gt;Don't get me wrong, temporary knowledge is essential. It's a bit like shallow and deep work. Our jobs entail both. We need the WHATs, in other words, the concrete implementations, to perform a task. At the same time, we should be contemplating the HOWs and WHYs behind them.&lt;/p&gt;

&lt;p&gt;One can write code without critical or creative thinking, but it doesn't mean they should do it. Copy-paste from StackOverflow, anybody?&lt;/p&gt;

&lt;p&gt;The problem is, by disregarding practices and principles, we are condemned to start from scratch and re-learn at every context switch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I fear not the man who has practiced 10,000 kicks once, but I fear the man who has practiced one kick 10,000 times.&lt;/p&gt;

&lt;p&gt;–Bruce Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Permanent Knowledge
&lt;/h2&gt;

&lt;p&gt;Permanent knowledge does not expire because it's not bound to the current context. But it has a price. Given the higher level of generalization, it's often more challenging to acquire. It's a bit like in code, its easier to start with a concretion and, later, extract the abstraction.&lt;/p&gt;

&lt;p&gt;Thus, one way to develop permanent knowledge would be laddering up from temporary knowledge. Instead of throwing arrays at problems, understand HOW to use them, most importantly, understand WHY. Syntax changes from language to language, but an array's nature is the same across the board. Hell, you could follow the abstraction to math, then it would apply to everything, not only software.&lt;/p&gt;

&lt;p&gt;Recently, I started working on a &lt;a href="https://odone.me/posts/2020-07-10-grateful-for-the-opportunity-of-working-on-legacy-code.html" rel="noopener noreferrer"&gt;legacy project that is over-engineered with all sorts of technologies&lt;/a&gt;. What saved me was transferring knowledge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Haskell -&amp;gt; RxJs&lt;/li&gt;
&lt;li&gt;Redux -&amp;gt; NgRx&lt;/li&gt;
&lt;li&gt;Elm -&amp;gt; TypeScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not knowing Haskell, Redux, and Elm that enabled me to thrive in this project. It's the fact that I understand the principles behind those technologies. They are just an incarnation of category theory, reactive functional programming, and strongly-typed functional programming.&lt;/p&gt;

&lt;p&gt;At the end of the day, we need a concrete implementation (temporary knowledge). But we cannot do a good job if we are not guided by permanent knowledge. I can easily search the details of an algorithm on a tree if I know it exists. If I don't, I'm going to re-invent the wheel, and possibly screw it up.&lt;/p&gt;

&lt;p&gt;I feel I should close this post with a joke. Something about a JavaScript developer trying to convince a Ruby developer that the data structure they need is an object, not a hash. But I fear it would reflect the reality of the silos created by temporary knowledge around us.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>essentialskills</category>
      <category>learning</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Making an Endpoint 13 Times Faster</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Wed, 23 Sep 2020 07:35:56 +0000</pubDate>
      <link>https://dev.to/riccardoodone/making-an-endpoint-13-times-faster-5g4e</link>
      <guid>https://dev.to/riccardoodone/making-an-endpoint-13-times-faster-5g4e</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-09-23-making-an-endpoint-13-times-faster.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;p&gt;Last week I prepared a &lt;a href="https://odone.me/posts/2020-09-16-how-to-investigate-performance-issues-in-a-web-app-with-a-simple-script.html" rel="noopener noreferrer"&gt;script to reproduce performance issues&lt;/a&gt;. The goal was to populate the database with realistic data to test speed and UX on development and staging. As a result, we realized &lt;strong&gt;some screens in the application not only are slow, but they also break when displaying a big dataset&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We started working on the page that lists all the orders submitted by customers. The script inserts in the database an incremental number of them. Since in the screen it's possible to filter by date, this setup allowed us to &lt;strong&gt;check how fast the application loads compared to the number of items&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;25 orders took about 8 seconds;&lt;/li&gt;
&lt;li&gt;50 orders took about 12 seconds;&lt;/li&gt;
&lt;li&gt;100 orders took about 18 seconds;&lt;/li&gt;
&lt;li&gt;around 1000 orders, the application crashed with "Memory quota vastly exceeded".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a developer, my first instinct was to jump in and make it fast. But there were two issues with that approach: identifying &lt;strong&gt;what problem to solve&lt;/strong&gt;, and &lt;strong&gt;defining what &lt;em&gt;fast&lt;/em&gt; means&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even if it were technically feasible &lt;strong&gt;loading thousands of items fast enough, would it even make sense?&lt;/strong&gt; In our case, we don't think so. Given our understanding of the users, we believe they would interact with the list by using the search field to drill down to one specific order. In other words, they would rarely scroll the entire list, especially when hundreds of items were displayed.&lt;/p&gt;

&lt;p&gt;By taking a look at analytics, we identified &lt;strong&gt;where the application spends time&lt;/strong&gt; rendering the list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;about 85% on the backend (more than 90% time is spent in the JSON serializer);&lt;/li&gt;
&lt;li&gt;about 15% on the frontend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given this is a &lt;a href="https://odone.me/posts/2020-07-10-grateful-for-the-opportunity-of-working-on-legacy-code.html" rel="noopener noreferrer"&gt;rescue project&lt;/a&gt;, we are aiming for good enough, not perfect. But &lt;strong&gt;what does fast enough mean in our context?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We went back to staging and loaded different amounts of orders on the screen to get a feel for it. There are more scientific ways of defining a performance budget, but we didn't want to overengineer.&lt;/p&gt;

&lt;p&gt;With the user's need in mind, understanding the performance issues, and a definition of fast, I decided to explore solutions by &lt;strong&gt;spiking code&lt;/strong&gt;. I had assumptions on what and how to optimize, but I wanted to validate the solution before going all-in.&lt;/p&gt;

&lt;p&gt;I coded the JSON serializer from scratch, removed the unneeded data sent to the browser, paginated the endpoint, and removed the n+1 queries. This got us to 900 milliseconds, regardless of the search criteria. Since each page contains 50 items, we are &lt;strong&gt;more than 13 times faster&lt;/strong&gt; in the worst case (i.e., 12000/900) and &lt;strong&gt;infinitely faster&lt;/strong&gt; in the best case (i.e., the application does not crash).&lt;/p&gt;

&lt;p&gt;The spike was successful. Now it's time to save the git diff, reset hard, and rewrite from scratch with the proper discipline.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>learning</category>
      <category>legacy</category>
      <category>performance</category>
    </item>
    <item>
      <title>How to Investigate Performance Issues in a Web App with a Simple Script</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Wed, 16 Sep 2020 07:44:51 +0000</pubDate>
      <link>https://dev.to/riccardoodone/how-to-investigate-performance-issues-in-a-web-app-with-a-simple-script-1dmg</link>
      <guid>https://dev.to/riccardoodone/how-to-investigate-performance-issues-in-a-web-app-with-a-simple-script-1dmg</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-09-16-how-to-investigate-performance-issues-in-a-web-app-with-a-simple-script.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;What happens when you build for several months without testing with real users and real data? An application that doesn't work with real users and real data happens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is what I wrote in my &lt;a href="https://odone.me/posts/2020-09-10-99-percent-done.html" rel="noopener noreferrer"&gt;rant about 99 Percent Done&lt;/a&gt;. But I'm tasked with being &lt;a href="https://odone.me/posts/2020-07-10-grateful-for-the-opportunity-of-working-on-legacy-code.html" rel="noopener noreferrer"&gt;part of the solution not part of the problem&lt;/a&gt;. So let's make performance right.&lt;/p&gt;

&lt;p&gt;The first step to solve any coding issue is to be able to reproduce at will. In this case, the endpoints are slow, and I'm ready to bet all-in that it's due to database calls and JSON rendering. Not only that, the application monitoring service we employ draws the same conclusions. Thus, I could jump right in, gold master test, and rewrite queries and serializers. However, there are a couple of problems with this approach.&lt;/p&gt;

&lt;p&gt;First of all, I would need to populate the database with enough data to perform a meaningful performance (and UX) test. It's not feasible to fill thousands of forms by hand to reproduce every time. A small, unrealistic dataset would not work either for obvious reasons.&lt;/p&gt;

&lt;p&gt;Secondly, I don't want to optimize the entire application. It would take forever, and, at the moment, we are validating the product, not perfecting it. Said differently, I want to fix performance problems in places where users will spend most of their time, not optimize a screen they will rarely use.&lt;/p&gt;

&lt;p&gt;Lastly and most importantly, I need to share performance issues with the rest of the team. We have to experience features to decide when they are both fast enough and usable. Technically the application could render thousands of things on the screen, but the user experience would likely suffer.&lt;/p&gt;

&lt;p&gt;Given the &lt;a href="https://odone.me/posts/2020-08-28-how-to-tame-complexity-into-simplicity-with-a-shake-list.html" rel="noopener noreferrer"&gt;shake list&lt;/a&gt; mentioned above, I got a bunch of options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tweak performance without any feedback. Nope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manually fill thousands of forms. Hell, no!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some sort of database seeds on development and staging. This would yield the most fine-grained control. In fact, too much. I would have to understand the complex network of records each form submission generates and recreate it manually. Too error-prone and time-consuming.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Call controllers behind the endpoints directly. This seems sexy because I wouldn't need to get acquainted with implementation details, but mingling with the web framework's internals feels hacky.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If only I could simulate a user but faster. Wait a second! A person clicking things in a browser just sends HTTP requests to the server.&lt;/p&gt;

&lt;p&gt;I can copy the requests from the network tab in the dev tools, translate them into code, and wrap each request in loops and conditionals. Moreover, there are services to translate cURL commands into another language (search "curl to MY_LANGUAGE"), and copying cURL requests is one right-click away:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4t68x065rgobivm7lbi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4t68x065rgobivm7lbi.png" alt="Screenshot of the menu that appears when right-clicking a request in the dev tool's network tab" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Too good to be true, let's analyze the cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Coarser-grained control. I can either call an endpoint or not; there's no middle ground. This is a plus because it's what actual users do. Also, if I need to interact with the database, I could connect to it without a problem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automated emails. Luckily the framework allows disabling all deliveries with a boolean flag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Third-party API calls (e.g., payments and tax calculations). I'm in luck again because orders can be marked as tax-free and paid in cash to avoid third-party service requests. This makes things a bit less realistic, but we could manually test those specific cases or develop more sophistication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication. The application uses tokens, so with a couple of additional requests, I can authenticate the script.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great, there are no blockers. On the plus side, there are a ton of pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Easy to add new requests. Open the browser, simulate the user, copy the cURL, paste them in the script. We could even ask users to send that along as a way to reproduce things.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test memory leaks. Among other issues, this application leaks memory, and the script can help investigate that problem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test concurrency. Requests could be performed in parallel to load test the application as described in &lt;a href="https://medium.com/@riccardoodone/facing-concurrency-in-web-apps-guided-by-tests-3f5488f62607" rel="noopener noreferrer"&gt;Facing Concurrency in Web Apps Guided by Tests&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measure. I could time each request to verify the performance tweaks do work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Feedback. Anyone on the team can navigate to a screen and feel for its speed and usability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We are the users. Performance can be measured into a single number, but UX is interaction, not math, at the end of the day. By putting ourselves in the shoes of the customers, we can make better decisions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This looks promising. I'll let you know in the &lt;a href="http://odone.me/#newsletter" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt; whether I crashed and burned or if I can move to rant about the next legacy issue in this codebase.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>learning</category>
      <category>performance</category>
    </item>
    <item>
      <title>99 Percent Done</title>
      <dc:creator>Riccardo Odone</dc:creator>
      <pubDate>Thu, 10 Sep 2020 07:11:14 +0000</pubDate>
      <link>https://dev.to/riccardoodone/99-percent-done-2gof</link>
      <guid>https://dev.to/riccardoodone/99-percent-done-2gof</guid>
      <description>&lt;p&gt;You can keep reading here or &lt;a href="https://odone.me/posts/2020-09-10-99-percent-done.html" rel="noopener noreferrer"&gt;jump to my blog&lt;/a&gt; to get the full experience, including the wonderful pink, blue and white palette.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;It's 99% done&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is what I've heard on the first day of a new project months ago.&lt;/p&gt;

&lt;p&gt;I'm happy to report that, today, after several weeks of work, we are still 99% percent done. Or maybe I should say 99.9% done. Math is awesome, there's always room to add yet another nine to make it right.&lt;/p&gt;

&lt;p&gt;I have enough experience to know that when &lt;em&gt;99%&lt;/em&gt; or &lt;em&gt;almost&lt;/em&gt; are put in front of the word done, they actually mean &lt;em&gt;nowhere close&lt;/em&gt;. &lt;strong&gt;The issue is that &lt;em&gt;done&lt;/em&gt; does not mean anything until it's given a definition people can refer to&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When hard things surface, having a blurry definition (or none at all) justifies doing the easy work instead of the right one. Moving a ticket to a column named Done does not make the project one ticket more successful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We need a definition of done, otherwise...&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ...There's No Finish Line
&lt;/h2&gt;

&lt;p&gt;This product was supposed to be launched years ago. But without a definition of done, it was always not done, by definition.&lt;/p&gt;

&lt;p&gt;Being a commercial endeavor, profit should have been the success metric guiding the effort. Instead, without boundaries, the product grew to a &lt;a href="https://odone.me/posts/2020-07-10-grateful-for-the-opportunity-of-working-on-legacy-code.html" rel="noopener noreferrer"&gt;complicated aggregate of features&lt;/a&gt; that accrued bugs and welcomed scope creep.&lt;/p&gt;

&lt;h2&gt;
  
  
  ...Developers Just Translate Specifications
&lt;/h2&gt;

&lt;p&gt;Don't get me wrong, coding is awesome but I'm tired of building things that have zero positive impact. When developers are tasked with translating specifications into code, they will just do that. Or worse.&lt;/p&gt;

&lt;p&gt;If people are not given the incentive to care about the product, they will have fun elsewhere. For example, by &lt;a href="https://odone.me/posts/2020-04-23-learning-commercial-projects.html" rel="noopener noreferrer"&gt;introducing accidental complexity&lt;/a&gt;. Or at least that's what I did in the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  ...We are Building the Wrong Thing
&lt;/h2&gt;

&lt;p&gt;A ticket containing just a specification without any additional context can be moved forward in two ways: by doing a bad job or by doing a perfect one. Since the latter is unattainable, the result is destined to suck.&lt;/p&gt;

&lt;p&gt;If we don't know who's going to use the feature and how, the only way to develop it is to cover all possible cases, many of which conflicting. Good luck with that!&lt;/p&gt;

&lt;h2&gt;
  
  
  ...We are Building the Wrong Thing Wrong
&lt;/h2&gt;

&lt;p&gt;What happens when you build for several months without testing with real users and real data? An application that doesn't work with real users and real data happens.&lt;/p&gt;

&lt;p&gt;But it works on my machine. Yes, in development, with one user, and three products. It's going to be fun to fix months of work done on top of a slow infrastructure that leaks memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have a Definition of Done in the Ticket
&lt;/h2&gt;

&lt;p&gt;I have a wild idea. Maybe we should keep the definition of done in the ticket and let everybody know what we are trying to achieve.&lt;/p&gt;

&lt;p&gt;Yes, that means we need to &lt;strong&gt;figure out what we are trying to do before we do it&lt;/strong&gt;. But it doesn't seem such a bad idea.&lt;/p&gt;




&lt;p&gt;Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my &lt;a href="https://odone.me#newsletter" rel="noopener noreferrer"&gt;PinkLetter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
