<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>RSS Feed for Short Ruby Email Courses</title>
    <description>Short Ruby Email Courses - follow my journey while building the email courses</description>
    <link>https://learn.shortruby.com/blog/feed</link>
    <item>
      <title>Micro benchmarking value objects in Ruby</title>
      <description>&amp;lt;p&amp;gt;As I was working on another email part of my &amp;lt;a href=&amp;quot;https://learn.shortruby.com/courses/modern-ruby&amp;quot;&amp;gt;Modern Ruby course via email&amp;lt;/a&amp;gt; I wanted to make some micro benchmarks on &amp;lt;code&amp;gt;Data.define&amp;lt;/code&amp;gt; vs &amp;lt;code&amp;gt;Struct&amp;lt;/code&amp;gt; vs &amp;lt;code&amp;gt;OpenStruct&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;They are not a production-level benchmark, so take them with a grain of salt.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I added all code and results in a repo at &amp;lt;a href=&amp;quot;https://github.com/lucianghinda/value-object-in-ruby-benchmarks&amp;quot;&amp;gt;https://github.com/lucianghinda/value-object-in-ruby-benchmarks&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Creating new objects&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;When creating a new object, &amp;lt;code&amp;gt;Struct&amp;lt;/code&amp;gt; (with &amp;lt;code&amp;gt;keyword_init: true&amp;lt;/code&amp;gt;)and &amp;lt;code&amp;gt;Data.define&amp;lt;/code&amp;gt; behave almost the same (the differences are with error margin or so small that they are probably due to my setup), while &amp;lt;code&amp;gt;OpenStruct&amp;lt;/code&amp;gt; seems to be the slowest.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Having defines the following keys and values:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;1000&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;map&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;key&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;#{&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;to_sym&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;values&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;1000&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;map&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;value&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;#{&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys_and_values&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Hash&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;zip&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;values&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)]&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;The creation benchmarks are testing the following code:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;DataStruct&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Struct&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;keyword_init: &amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;DataStruct&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;**&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys_and_values&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# vs&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;DataDefine&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Data&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;define&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;DataDefine&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;**&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys_and_values&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# vs&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;OpenStruct&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;**&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys_and_values&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Here is a &amp;lt;code&amp;gt;`bmbm`&amp;lt;/code&amp;gt; benchmark result:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;Creating a new object - Benchmark with bmbm
Rehearsal &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;--------------------------------------------------&amp;lt;/span&amp;gt;
Struct.new       0.000023   0.000003   0.000026 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000024&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
Data.define      0.000020   0.000001   0.000021 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000022&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
OpenStruct.new   0.001705   0.000075   0.001780 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.001780&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;-----------------------------------------&amp;lt;/span&amp;gt; total: 0.001827sec

                     user     system      total        real
Struct.new       0.000020   0.000000   0.000020 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000020&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
Data.define      0.000022   0.000000   0.000022 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000022&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
OpenStruct.new   0.001069   0.000044   0.001113 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.001132&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Here is the &amp;lt;code&amp;gt;ibs&amp;lt;/code&amp;gt; benchmark result:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;Creating a new object - Benchmark with ips
ruby 3.3.0 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;2023-12-25 revision 5124f9ac75&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;[&amp;lt;/span&amp;gt;arm64-darwin23]
Warming up &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;--------------------------------------&amp;lt;/span&amp;gt;
          Struct.new     5.169k i/100ms
         Data.define     5.361k i/100ms
      OpenStruct.new    62.000 i/100ms
Calculating &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;-------------------------------------&amp;lt;/span&amp;gt;
          Struct.new     50.086k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;± 1.7%&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; i/s -    253.281k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;in   &amp;lt;/span&amp;gt;5.058450s
         Data.define     51.646k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;± 1.1%&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; i/s -    262.689k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;in   &amp;lt;/span&amp;gt;5.086990s
      OpenStruct.new    607.447 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;± 0.8%&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; i/s -      3.038k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;in   &amp;lt;/span&amp;gt;5.001584s

Comparison:
         Data.define:    51646.3 i/s
          Struct.new:    50085.7 i/s - 1.03x  slower
      OpenStruct.new:      607.4 i/s - 85.02x  slower
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Here is the &amp;lt;code&amp;gt;memory&amp;lt;/code&amp;gt; benchmark result.&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;Creating a new object - Benchmark with ips
Calculating &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;-------------------------------------&amp;lt;/span&amp;gt;
          Struct.new    36.792k memsize &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
                         2.000  objects &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
                         0.000  strings &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
         Data.define    36.792k memsize &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
                         2.000  objects &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
                         0.000  strings &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
      OpenStruct.new   848.728k memsize &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
                         8.005k objects &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
                        50.000  strings &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;     0.000  retained&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;

Comparison:
          Struct.new:      36792 allocated
         Data.define:      36792 allocated - same
      OpenStruct.new:     848728 allocated - 23.07x more
&amp;lt;/pre&amp;gt;
&amp;lt;h2&amp;gt;Accessing attributes&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;Again &amp;lt;code&amp;gt;Data.define&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Struct&amp;lt;/code&amp;gt; with keyword arguments are the same. On the other side &amp;lt;code&amp;gt;OpenStruct&amp;lt;/code&amp;gt; is almost twice as slow.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Having the following data defined:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;1000&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;map&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;key&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;#{&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;to_sym&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;values&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;1000&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;map&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;value&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;#{&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;i&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys_and_values&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Hash&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;zip&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;values&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)]&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;And then defining the following structures:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;BigDataS&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Struct&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;keyword_init: &amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;BigDataD&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Data&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;define&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;The benchmarks are comparing:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;each&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;struct_object&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;send&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;_1&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;each&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;data_object&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;send&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;_1&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;keys&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;each&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;opens_struct_object&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;send&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;_1&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Here is the &amp;lt;code&amp;gt;bmbm&amp;lt;/code&amp;gt; benchmark result:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;Accessing attributes - bmbm &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;test
&amp;lt;/span&amp;gt;Rehearsal &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;-----------------------------------------------&amp;lt;/span&amp;gt;
Struct        0.000069   0.000002   0.000071 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000071&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
Data.define   0.000069   0.000003   0.000072 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000071&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
OpenStruct    0.000110   0.000003   0.000113 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000116&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;--------------------------------------&amp;lt;/span&amp;gt; total: 0.000256sec

                  user     system      total        real
Struct        0.000049   0.000001   0.000050 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000046&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
Data.define   0.000046   0.000001   0.000047 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000046&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
OpenStruct    0.000091   0.000001   0.000092 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;  0.000094&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Here is the &amp;lt;code&amp;gt;ibs&amp;lt;/code&amp;gt; benchmark result:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;Accessing attributes - ips &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;test
&amp;lt;/span&amp;gt;ruby 3.3.0 &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;2023-12-25 revision 5124f9ac75&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;[&amp;lt;/span&amp;gt;arm64-darwin23]
Warming up &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;--------------------------------------&amp;lt;/span&amp;gt;
              Struct     2.857k i/100ms
         Data.define     2.828k i/100ms
          OpenStruct     1.384k i/100ms
Calculating &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;-------------------------------------&amp;lt;/span&amp;gt;
              Struct     28.420k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;± 0.9%&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; i/s -    142.850k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;in   &amp;lt;/span&amp;gt;5.026906s
         Data.define     28.691k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;± 0.5%&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; i/s -    144.228k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;in   &amp;lt;/span&amp;gt;5.027131s
          OpenStruct     13.475k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;± 0.9%&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; i/s -     67.816k &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;in   &amp;lt;/span&amp;gt;5.033315s

Comparison:
         Data.define:    28690.8 i/s
              Struct:    28419.6 i/s - same-ish: difference falls within error
          OpenStruct:    13474.6 i/s - 2.13x  slower
&amp;lt;/pre&amp;gt;
&amp;lt;h2&amp;gt;Context for understanding why Data.define and Struct are similar&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://ruby.social/@ufuk&amp;quot;&amp;gt;Ufuk Kayserilioglu&amp;lt;/a&amp;gt; &amp;lt;a href=&amp;quot;https://ruby.social/@ufuk/112141972493634321&amp;quot;&amp;gt;explains&amp;lt;/a&amp;gt; why &amp;lt;code&amp;gt;Data.define&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Struct&amp;lt;/code&amp;gt; with keyword arguments have the same behavior:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Ufuk explaining: &amp;quot;Data is (basically) just Struct with no writer methods defined (and a freeze, I believe). The CRuby codepaths are exactly the same for both, except zverok decided that Data#initialize should always accept kw arguments, so Data.new has to convert positional args to kw args before passing them to initialize.&amp;quot;&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/data-define-similar-with-struct.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;A note about &amp;lt;code&amp;gt;OpenStruct&amp;lt;/code&amp;gt;&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/byroot&amp;quot;&amp;gt;Jean Boussier&amp;lt;/a&amp;gt; &amp;lt;a href=&amp;quot;https://twitter.com/_byroot/status/1771221003051422101&amp;quot;&amp;gt;answered&amp;lt;/a&amp;gt; a question about why OpenStruct is so slow:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Jean Boussier explaining &amp;quot;It&amp;#39;s not because it&amp;#39;s written in Ruby but because of it&amp;#39;s semantic.  For every single instance it has to create a metaclass and define methods on it. It&amp;#39;s terribly wasteful and doing the same in C wouldn&amp;#39;t be much faster.  OpenStruct should be considered deprecated really.&amp;quot;&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/open-struct-deprecated.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-03-29</pubDate>
      <link>https://learn.shortruby.com/blog/micro-benchmarking-data-define-vs-struct-vs-openstruct</link>
      <guid>https://learn.shortruby.com/blog/micro-benchmarking-data-define-vs-struct-vs-openstruct</guid>
    </item>
    <item>
      <title>History of the endless method syntax</title>
      <description>&amp;lt;p&amp;gt;When I learn about a new language feature, I like to read and discuss the proposal. How and why it was accepted. What was the requester trying to accomplish, and what problem did they try to solve?&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here, I will review how the endless method was introduced in the Ruby language.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;What is the definition of the endless method?&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;In a few words, it is an alternative syntax for defining a method that consists of a single expression.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here is the definition from &amp;lt;a href=&amp;quot;https://docs.ruby-lang.org/en/master/syntax/methods_rdoc.html&amp;quot;&amp;gt;Ruby documentation&amp;lt;/a&amp;gt;:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Definition of endless method in Ruby&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/definition-from-ruby-docs.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here are two simple examples:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;exists?&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;User&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;where&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;organisation: &amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;organisation&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;).&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;exists?&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;or with parameters:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;format_date&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;date&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;date&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;strftime&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;DEFAULT_DATE_FORMAT&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;h2&amp;gt;History of trying to remove one or more &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;s&amp;lt;/h2&amp;gt;

&amp;lt;h3&amp;gt;2011 - the first proposal is made mainly as a joke, but it tries to solve a real problem with idiomatic code&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;In 2011, Yasushi Ando &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/5054&amp;quot;&amp;gt;proposed&amp;lt;/a&amp;gt; (as a joke) an &amp;lt;code&amp;gt;en(n+)d&amp;lt;/code&amp;gt; to solve the following issue: having too many ends:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# Source: https://bugs.ruby-lang.org/issues/5054&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# Replace this code:&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;module&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;MyModule&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;MyClass&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;my_method&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;10&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;rand&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;0.5&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;:small&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# With:&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;module&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;MyModule&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;MyClass&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;my_method&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;10&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;rand&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;0.5&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;:small&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;ennnnnd&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;While the proposal was a joke, the idea is that sometimes the number of &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;s we have to write is high (sometimes it takes more chars than the actual business logic).&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Another &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/5065&amp;quot;&amp;gt;proposal&amp;lt;/a&amp;gt; (this time a serious one) was made by Lazaridis Ilias later the same year, 2011. This time the proposal was to allow &amp;lt;code&amp;gt;}&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# Source: https://bugs.ruby-lang.org/issues/5065&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;module&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;MyModule&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;MyClass&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;my_method&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;10&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt;   &amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# &amp;quot;10.times do&amp;quot; would work, too&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;rand&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;0.5&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;:small&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;This one was rejected. Among other proposed ideas was to have an &amp;lt;code&amp;gt;endall&amp;lt;/code&amp;gt; as a keyword that will add all necessary ends if I might say so:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# Source: https://bugs.ruby-lang.org/issues/5065&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;module&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;MyModule&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;MyClass&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;my_method&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;10&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;rand&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;0.5&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;:small&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;endall&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;h3&amp;gt;2016 - another proposal was made, this time a serious one&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Then, in 2016, Nobuyoshi Nakada &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/12241&amp;quot;&amp;gt;proposed&amp;lt;/a&amp;gt; another idea, building upon the previous one: to introduce the &amp;lt;code&amp;gt;end!&amp;lt;/code&amp;gt; keyword like a &amp;lt;code&amp;gt;super end&amp;lt;/code&amp;gt; concept that will end all blocks not ended until its level:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# Source: https://bugs.ruby-lang.org/issues/12241&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;module&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;MyModule&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;class&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;MyClass&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;my_method&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;10&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;rand&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;0.5&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;p&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;:small&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;!&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;There were few replies to it, so there was no final decision.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;2020 - a new proposal with more arguments and a simple approach is suggested&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;The &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/16746&amp;quot;&amp;gt;feature request&amp;lt;/a&amp;gt; for the current format was submitted in 2020 by Yusuke Endoh on the Ruby issue tracker:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Endless method definition - proposal from Yusuke in Ruby tracker&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/endless-method-yusuke-proposal.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The initial proposal wanted to use &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt; when defining the method body, but Matz &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/16746#note-7&amp;quot;&amp;gt;proposed&amp;lt;/a&amp;gt; using &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt;. Still, the go-ahead for running an experiment like this was given at the end of 2020.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Later in that thread, Yusuke &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/16746#note-8&amp;quot;&amp;gt;presented&amp;lt;/a&amp;gt; some arguments:&amp;lt;/p&amp;gt;

&amp;lt;blockquote&amp;gt;
&amp;lt;p&amp;gt;Surprisingly, according to the following rough estimate of ruby/ruby code base, this kind of simple method definitions account for 24% of the entire method definitions.&amp;lt;/p&amp;gt;
&amp;lt;/blockquote&amp;gt;

&amp;lt;p&amp;gt;Victor Shepelev &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/16746#note-25&amp;quot;&amp;gt;added&amp;lt;/a&amp;gt; some excellent arguments in favor of this proposal:&amp;lt;/p&amp;gt;

&amp;lt;blockquote&amp;gt;
&amp;lt;p&amp;gt;The most precious Ruby&amp;rsquo;s quality for me is &amp;ldquo;expressiveness at the level of the single &amp;lsquo;phrase&amp;rsquo; (expression)&amp;rdquo;, and it is not &amp;ldquo;code golf&amp;rdquo;-expressiveness, but rather structuring language&amp;rsquo;s consistency around the idea of building phrases with all related senses packed, while staying lucid about the intention&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I believe that the difference of &amp;ldquo;how you write it when there is one statement&amp;rdquo; vs &amp;ldquo;&amp;hellip;more than one statement&amp;rdquo; is intentional, and fruitful: &amp;ldquo;just add one more line to 10-line method&amp;rdquo; typically small addition, but &amp;ldquo;just add one more line to 1-line method&amp;rdquo; frequently makes one think: may be that&amp;rsquo;s not what you really need to do, maybe data flow becomes unclear&amp;lt;/p&amp;gt;
&amp;lt;/blockquote&amp;gt;

&amp;lt;h3&amp;gt;2021 - a new proposal is made for a super-end keyword&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;While the status of the Yusuke proposal kept having multiple arguments, and Matz already proposed a syntax there that he would like but said might be conflicting, a new proposal was submitted trying to solve the same issue but focusing on nested ends.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;This &amp;lt;a href=&amp;quot;https://bugs.ruby-lang.org/issues/17786&amp;quot;&amp;gt;proposal&amp;lt;/a&amp;gt; added by Jabari Zakiya is about the same idea of how to reduce the number of &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; keywords, and in this case, the proposal was to use &amp;lt;code&amp;gt;ends&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# Source: https://bugs.ruby-lang.org/issues/17786&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;render&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;scene&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;image&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenWidth&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenHeight&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenHeight&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;y&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenWidth&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;color&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;self&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;traceRay&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;....&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;r&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;g&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;b&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Color&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;toDrawingColor&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;color&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;image&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;set&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;y&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;StumpyCore&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;::&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;RGBA&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;from_rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;r&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;g&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;b&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;))&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# To be replaced by&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;render&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;scene&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;image&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenWidth&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenHeight&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenHeight&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;y&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;screenWidth&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;times&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;color&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;self&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;traceRay&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;....&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;r&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;g&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;b&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;Color&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;toDrawingColor&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;color&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;image&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;set&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;y&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;StumpyCore&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;::&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;RGBA&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;from_rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;r&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;g&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;b&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;))&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;ends&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;This, too, not approved yet. Notice this is a bit of a tangent to the endless method, but it is worth considering that the idea that the number of &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; keywords written should be limited is present.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Ruby 3.0 - release on 25 December 2020 included endless method definition&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Ruby version 3.0 included the endless method definition or shorthand method syntax with the following format:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;params&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;body&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# or&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;params&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;an&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;expression&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;that&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;can&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;be&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;on&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;multiple&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;lines&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# or&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;body&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# or&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;def&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;an&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;expression&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;that&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;can&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;be&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;on&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;multiple&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;lines&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Of course, having arguments is optional, but if you decide to specify them, then parentheses are mandatory.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;More about the endless method&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;One of the best resources to read about this in a format that does an excellent assessment of this feature is the article written by Victor Shepelev called &amp;lt;a href=&amp;quot;https://zverok.space/blog/2023-12-01-syntax-sugar5-endless-methods.html&amp;quot;&amp;gt;“Useless Ruby sugar”: Endless (one-line) methods&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Curious to learn more about this?&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;If you want to learn more about this feature, its gotchas, when and how to use it, and real code examples, I am creating a course called &amp;lt;a href=&amp;quot;https://learn.shortruby.com/courses/modern-ruby&amp;quot;&amp;gt;“Modern Ruby Syntax”&amp;lt;/a&amp;gt;, where I explore the endless method along with other Ruby features.&amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-03-19</pubDate>
      <link>https://learn.shortruby.com/blog/history-of-endless-method</link>
      <guid>https://learn.shortruby.com/blog/history-of-endless-method</guid>
    </item>
    <item>
      <title>Modern Ruby Syntax - update 1</title>
      <description>&amp;lt;p&amp;gt;Here’s a quick update on my progress with the email course for &amp;lt;a href=&amp;quot;https://learn.shortruby.com/courses/modern-ruby&amp;quot;&amp;gt;Modern Ruby Syntax&amp;lt;/a&amp;gt;:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;I finished the numbered block params chapter (the first email in course)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;I am close to finishing the draft for the endless method syntax email (the second one in the same course).&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;In the last two or three weeks, the progress has been slower than I expected because I worked to write some proposals for a couple of Ruby conferences in Europe, where I hope to speak about the Modern Ruby Syntax. Fingers crossed!&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Numbered Block Params&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;The first round of feedback on the numbered block params email has been received, and I&amp;rsquo;m actively working on updates.
It should be ready in an alpha state by next week.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The PDF version of the document, which is the most detailed version, is nearly 20 pages long. It covers everything from explaining numbered block parameters to providing examples of how to use them in your daily coding life. Additionally, it includes some style guidelines that I believe will help you write more readable code when working with numbered block parameters.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;No worries, there email will be shorter as you always have the PDF attached for a deep dive.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Your feedback is invaluable!&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;If you&amp;rsquo;re interested in proofreading or just feedback on the numbered block params email, please email me at
lucian@shortruby.com. I&amp;rsquo;ll send you the PDF and eagerly await your insights.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Endless Method Syntax&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;I worked this week on writing the email for the endless method syntax. It is 80% done.
I will also work on this one next week, try to finish it, and prepare it for feedback.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;A video overview&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;In case you want to see a sneak peek at the content, I made a short video with the current status and talk about what each chapter contains:&amp;lt;/p&amp;gt;

&amp;lt;iframe width=&amp;quot;560&amp;quot; height=&amp;quot;315&amp;quot; src=&amp;quot;https://www.youtube.com/embed/xIjdatejD2M?si=Q0xQI7e3M99T22-W&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen class=&amp;quot;mx-auto&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;
</description>
      <pubDate>2024-02-09</pubDate>
      <link>https://learn.shortruby.com/blog/update-1-modern-ruby-syntax-finished-first-chapter</link>
      <guid>https://learn.shortruby.com/blog/update-1-modern-ruby-syntax-finished-first-chapter</guid>
    </item>
    <item>
      <title>The tech stack I choose to build email courses</title>
      <description>&amp;lt;p&amp;gt;Here are my choices about the tech stack I plan to use for this project.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Criteria&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;When I think about the tech stack I want to use for this project, I have to consider the following:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Time&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The most important part is time. This is a side project; thus, it receives a small part of my effort. This is a fixed allocation that I can put among spending time with family, having a full-time job, and taking time for myself. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I think about time in two ways: &amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;The time I have to build the project&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;The time I will have to maintain the project&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Speed of change&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The second most important thing is being able to change. That includes releasing new features or reacting to users’ needs or wants. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;What’s important here is the speed of change: How fast can I release something to production?&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Costs&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;On the third place are costs: hosting, emailing, and additional services used to build the project. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I have not yet allocated a budget for this project but will do so over the next weeks. Having a budget will help to validate if the idea is &amp;lt;em&amp;gt;feasible&amp;lt;/em&amp;gt; and &amp;lt;em&amp;gt;sustainable&amp;lt;/em&amp;gt; over the long run.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Choices&amp;lt;/h2&amp;gt;

&amp;lt;h3&amp;gt;Programming language: Ruby&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using &amp;lt;a href=&amp;quot;https://www.ruby-lang.org/en/&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ruby&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;. It is the language the I know best and it is also a language that allows me enough flexibility to write code that is easy to change and quick to adapt. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Web framework: Ruby on Rails&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using &amp;lt;a href=&amp;quot;https://rubyonrails.org&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Ruby on Rails&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;. I briefly considered Hanami 2 as it is fresh, and I wanted to pick it up, but considering the time and speed of change, there is not much room to learn a new framework. At the same time, Ruby on Rails comes with a lot of battery included, and that is what I need in this project where I plan to focus on the product and content. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Persistence: SQLite&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using &amp;lt;strong&amp;gt;SQLite&amp;lt;/strong&amp;gt;. Ruby on Rails now has good support for SQLite in production, and &amp;lt;a href=&amp;quot;https://fractaledmind.github.io&amp;quot;&amp;gt;Stephen Margheim&amp;lt;/a&amp;gt; has a lot of easy-to-follow and to-the-point articles for configuring it. Choosing SQLite reduces the time allocated to maintenance, so it is an easy win.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I am not concerned with scalability as I don’t see this project needing more than a server. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I also added the &amp;lt;a href=&amp;quot;https://github.com/fractaledmind/activerecord-enhancedsqlite3-adapter&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Activerecord-enhancedsqlite3-adapter&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; that adds support for generated columns, deferred foreign keys, PRAGMA tunning, and extensions loading.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;For backup I am using &amp;lt;a href=&amp;quot;https://github.com/fractaledmind/litestream-ruby&amp;quot;&amp;gt;&amp;lt;code&amp;gt;litestream&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; gem that makes working with the &amp;lt;a href=&amp;quot;https://litestream.io&amp;quot;&amp;gt;&amp;lt;code&amp;gt;litestream.io&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; easier.  &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Backround running jobs&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I use &amp;lt;a href=&amp;quot;https://github.com/oldmoe/litestack&amp;quot;&amp;gt;litestack&amp;lt;/a&amp;gt; gem that comes with everything, including Jobs, Caching, Search, and Metrics. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I am considering for jobs to move to &amp;lt;a href=&amp;quot;https://github.com/basecamp/solid_queue&amp;quot;&amp;gt;SolidQueue&amp;lt;/a&amp;gt; at some point. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Admin: Avo&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using &amp;lt;a href=&amp;quot;https://avohq.io&amp;quot;&amp;gt;Avo&amp;lt;/a&amp;gt; to build the Admin UI that I will use to manage the content. Avo looks excellent; it is modern and fast, and I don’t want to spend time building a UI for myself to manage the content. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;It is already built, it has a feel of Railsy while configuring it, and it is flexible enough so that if I need more custom things, I can do them. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The best benefit is that I get time back to invest in the content instead of the development of an admin. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;CSS: Tailwind and TailwindUI&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using &amp;lt;a href=&amp;quot;https://tailwindcss.com&amp;quot;&amp;gt;Tailwind&amp;lt;/a&amp;gt; because I used it professionally, so I know it a bit, and also because I bought &amp;lt;a href=&amp;quot;https://tailwindui.com&amp;quot;&amp;gt;TailwindUI&amp;lt;/a&amp;gt; where I can find ready-made components that I can copy/paste into my project if needed. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Rails 7 also comes with an already integrated Tailwind, and it works with import maps.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Import maps&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I like &amp;lt;a href=&amp;quot;https://github.com/rails/importmap-rails&amp;quot;&amp;gt;Rails import maps&amp;lt;/a&amp;gt;, and I am picking other UI-related gems only if it works with import maps. That is because I don’t want to run &amp;lt;code&amp;gt;nodejs&amp;lt;/code&amp;gt; or have any &amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt; installed. It adds to maintenance, and I like to minimize maintenance tasks. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Views: ERBs and Phlex&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;First, I will create the general views as HTML ERB files. This is because it is close to HTML, will have low maintenance, and can be changed quickly. It also helps with copying/pasting elements from TailwindUI without making too many changes. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I will organize (or extract) standard components by using &amp;lt;a href=&amp;quot;https://www.phlex.fun&amp;quot;&amp;gt;phlex&amp;lt;/a&amp;gt;. Phlex is closer to my backend background, so I can keep writing Ruby while building UI elements. &amp;lt;a href=&amp;quot;https://rubygems.org/gems/phlex-rails&amp;quot;&amp;gt;&amp;lt;code&amp;gt;phlex-rails&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; has also a good integration with &amp;lt;a href=&amp;quot;https://lookbook.build/guide/components/phlex&amp;quot;&amp;gt;&amp;lt;code&amp;gt;Lookbook&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; and so I can easily preview my components. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;How I use this mix is like this: the general structure of the page is done with HTML ERB, but the elements inside are mostly done with Phlex. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Testing&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I use &amp;lt;a href=&amp;quot;https://guides.rubyonrails.org/testing.html#rails-meets-minitest&amp;quot;&amp;gt;Minitest&amp;lt;/a&amp;gt;, the default testing framework for Rails. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I like Minitest in my side projects for two main reasons: &amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;It comes default with Rails and runs fast&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;It is not a DSL, but it is just Ruby code. So it will be easy to return to it and not spend time remembering a DSL. &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;There is one gem that I usually install for tests and that is &amp;lt;a href=&amp;quot;https://github.com/adammck/minitest-stub-const&amp;quot;&amp;gt;&amp;lt;code&amp;gt;minitest-stub-const&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt;. Stubbing constanta can also be done without this gem, but I kept using this gem. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Static pages: Sitepress&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;For static pages, like &amp;lt;a href=&amp;quot;https://learn.shortruby.com&amp;quot;&amp;gt;homepage&amp;lt;/a&amp;gt;, or &amp;lt;a href=&amp;quot;https://learn.shortruby.com/blog&amp;quot;&amp;gt;the blog section&amp;lt;/a&amp;gt; I am using &amp;lt;a href=&amp;quot;https://sitepress.cc&amp;quot;&amp;gt;sitepress&amp;lt;/a&amp;gt;. It has an easy integration with Rails and helps move from static files to dynamic options easily. I also like that it has Frontmatter support and comes with a way to work with each page’s meta-data. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;It is a maintenance and low configuration solution: Install it, write things in &amp;lt;code&amp;gt;.html.md&amp;lt;/code&amp;gt; files, and all will work. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Debugging: Debug gem&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/ruby/debug&amp;quot;&amp;gt;Debug&amp;lt;/a&amp;gt; gem is now the default debugger gem for Rails, and I like it a lot. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;A lot of development is happening, and new features are always added. I started moving away from &amp;lt;code&amp;gt;puts&amp;lt;/code&amp;gt; debugging to real debugging, and I even use the debugger to inspect object states while I do code design. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;As the &amp;lt;code&amp;gt;debug&amp;lt;/code&amp;gt; gem is part of Ruby, I think it can be a safe bet for the future with little maintenance or overhead required. I am using it in all my projects. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Asset pipeline: Propshaft&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;On multiple occasions, &amp;lt;a href=&amp;quot;https://github.com/rails/propshaft&amp;quot;&amp;gt;&amp;lt;code&amp;gt;propshaft&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; mentioned that it would become the default in Rails, so I chose it because I think it will make upgrading Rails easier. I also like that &amp;lt;code&amp;gt;propshaft&amp;lt;/code&amp;gt; is simple and does one thing well. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Beautiful web UX: Hotwire&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Of course, &amp;lt;a href=&amp;quot;https://hotwired.dev&amp;quot;&amp;gt;Hotwire&amp;lt;/a&amp;gt;  is my choice for building web UX. It comes with Rails by default, and it has a lot of good integrations. It allows me to build promising web UX without the mental overhead of learning more JS or a new JS library. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;When I need to write some JS, I like to use Stimulus because it gives me a place and a way to organize JS code. This is important for a project you don’t open daily to know exactly where to find each piece of code.  &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Environment variables: direnv&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I started using &amp;lt;a href=&amp;quot;https://direnv.net&amp;quot;&amp;gt;&amp;lt;code&amp;gt;direnv&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; a while back, mostly because I needed to share some environment variables between projects. So I can put a &amp;lt;code&amp;gt;.envrc&amp;lt;/code&amp;gt; file in the top folder for a group of projects and have those environment vars available in all subfolders. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;It removes the need to use the &amp;lt;code&amp;gt;dotenv&amp;lt;/code&amp;gt; gem and also decreases the chances of accidentally committing your &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt; file if you put the &amp;lt;code&amp;gt;.envrc&amp;lt;/code&amp;gt; in the parent folder and not the code folder.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I mostly organize my projects like this: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;projects/
project/shortruby/
project/shortruby/apps
project/shortruby/apps/shortruby.com
project/shortruby/apps/lean.shortruby.com
project/shortruby/apps/newsletter.shortruby.com
project/shortruby/images
project/shortruby/docs
project/shortruby/scripts
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;I can add the &amp;lt;code&amp;gt;.envrc&amp;lt;/code&amp;gt; file under &amp;lt;code&amp;gt;projects/shortruby/apps&amp;lt;/code&amp;gt; and have the common development environment variables shared between multiple projects. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Code Quality: Rubocop + Rubycritic&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I use Rubocop to enforce &amp;lt;a href=&amp;quot;https://github.com/lucianghinda/personal-ruby-style&amp;quot;&amp;gt;a coding style&amp;lt;/a&amp;gt; that I like and made for myself. It is well-supported by any editor and allows me to configure it as I see fit. In my case, I lean toward using new Ruby syntax so the cops are configured to allow that. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I added to it the following gems: &amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-rails&amp;quot;&amp;gt;rubocop-rails&amp;lt;/a&amp;gt; - “Automatic Rails code style checking tool”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-performance&amp;quot;&amp;gt;rubocop-performance&amp;lt;/a&amp;gt; - “A collection of RuboCop cops to check for performance optimizations in Ruby code”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-minitest&amp;quot;&amp;gt;rubocop-minitest&amp;lt;/a&amp;gt; - “Automatic Minitest code style checking tool”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-capybara&amp;quot;&amp;gt;rubocop-capybara&amp;lt;/a&amp;gt; - “Code style checking for Capybara test files (RSpec, Cucumber, Minitest)”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-rubycw&amp;quot;&amp;gt;rubocop-rubycw&amp;lt;/a&amp;gt; -  “Integrate RuboCop and ruby -cw. You can get Ruby&amp;rsquo;s warning as a RuboCop offense by rubocop-rubycw”&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;I also use &amp;lt;a href=&amp;quot;https://github.com/whitesmith/rubycritic&amp;quot;&amp;gt;Rubycritic&amp;lt;/a&amp;gt; to keep complexity low. I don’t refactor for every code smell, but it is good to have an excellent overview of what complexity each change might add to the existing codebase. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;You can see how I configured all these tools in my article about &amp;lt;a href=&amp;quot;https://learn.shortruby.com/blog/first-commits&amp;quot;&amp;gt;First commits&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Code editor: RubyMine&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using &amp;lt;a href=&amp;quot;https://www.jetbrains.com/ruby/&amp;quot;&amp;gt;&amp;lt;code&amp;gt;RubyMine&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt; as my primary code editor with &amp;lt;code&amp;gt;IdeaVim&amp;lt;/code&amp;gt; plugin enabled. I worked with it quite a lot in the last few years and am fast when using it. Helps with debugging and go to definition. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Versioning: Github&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I have some experience with both &amp;lt;a href=&amp;quot;https://github.com&amp;quot;&amp;gt;Github&amp;lt;/a&amp;gt; and &amp;lt;a href=&amp;quot;https://gitlab.com&amp;quot;&amp;gt;Gitlab&amp;lt;/a&amp;gt;. But I think GitHub is a good choice here due to its UI simplicity. I don’t want to spend time in configuration and the UI/UX choices are great. The PR flow is quick and easy to set up, and for my needs, I only use GitHub actions to run simple tasks. I think Gitlab has a more powerful CI but I don’t think I need it right now.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Sending emails: Postmark&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;For sending emails, I choose &amp;lt;a href=&amp;quot;https://postmarkapp.com&amp;quot;&amp;gt;Postmark&amp;lt;/a&amp;gt;. It is easy to set up and offers API-sending email and SMTP services. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I don’t know if Postmark will be used to send the course content. But it will be used to send all transactional emails related to the project. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I am considering &amp;lt;a href=&amp;quot;https://convertkit.com&amp;quot;&amp;gt;ConverKit&amp;lt;/a&amp;gt; to write and send the actual email content. This will depend on their API support for the features I plan to build for my users: restart an email course, choose periodicity, and more. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Hosting: Hetzner and Hatchbox&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I am using VPS &amp;lt;a href=&amp;quot;https://www.hetzner.com&amp;quot;&amp;gt;Hetzner&amp;lt;/a&amp;gt; as they have a good price and being in EU it helps me with registering the invoice to my accounting. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I am using deployment &amp;lt;a href=&amp;quot;https://hatchbox.io&amp;quot;&amp;gt;Hatchbox&amp;lt;/a&amp;gt; for ease of use. I enabled automatic deployments to make a deploy with &amp;lt;code&amp;gt;git push&amp;lt;/code&amp;gt; - simple and effective.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Analytics: Plausible&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I discovered &amp;lt;a href=&amp;quot;https://plausible.io&amp;quot;&amp;gt;Plausible&amp;lt;/a&amp;gt; while learning Elixir and Phoenix and started using it for all my side projects. I like the simplicity, and it is also a good price for what it is offering. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Image storage: Cloudflare R2&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I started looking for an AWS S3 alternative primarily due to the time spent configuring IAM and other stuff that always needed to be done when setting up a new account. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I discovered Cloudflare R2 last year (https://www.cloudflare.com/developer-platform/r2/), and I started using it. Again, I like the simplicity (it can be configured in minutes) and can be used for ActiveStorage. What I plan to build, where I will mostly upload and display images, worked perfectly and for an excellent price. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;AWS S3 could be better for more complex tasks, but if the only need is to upload images and display them publicly while being a solo developer, the R2 is a good choice.  &amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Conclusion&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;These are the tools I use and why I chose them to build &amp;lt;a href=&amp;quot;https://learn.shortruby.com&amp;quot;&amp;gt;the email courses project&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I don’t think there exists a thing called “the best tech stack,” but depending on my knowledge and purpose, I can create an excellent tech stack to help me deliver fast and with good code quality. &amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-24</pubDate>
      <link>https://learn.shortruby.com/blog/tech-stack</link>
      <guid>https://learn.shortruby.com/blog/tech-stack</guid>
    </item>
    <item>
      <title>End of Cycle 1 - Retrospective</title>
      <description>&amp;lt;p&amp;gt;I can conclude that Cycle 1 is finished. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The main focus of &amp;lt;a href=&amp;quot;cycle1-building-an-welcoming-place&amp;quot;&amp;gt;Cycle 1&amp;lt;/a&amp;gt; was “Building a welcoming place,” and I think I managed to achieve it. The bar was not that high anyway.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here is what I managed to implement:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Added RSS feed for blog&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Added meta-tags with OG images&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Transformed all images to WEBP format where possible (it seems that OG images do not support WEBP)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Fixed accessibility page speed report&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Added feature to subscribe when launch with ConvertKit&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Add automatic Sitemap generation&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;h2&amp;gt;Learnings&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;This time passed very quickly. That might be because the total amount of time I invested was around 4 hours for programming and 3-4 hours of writing. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I spent most of the programming time trying to implement integration tests for an article on the blog and ensure that the OG properties are correctly displayed. The chunk spent there was trying to make Rails load Sitepress from a path under &amp;lt;code&amp;gt;test/data/sitepress&amp;lt;/code&amp;gt; and then get a blog article from there instead of testing against a real article.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Learning:&amp;lt;/strong&amp;gt; Speed of development should be considered in a side project, and sometimes it is ok not to stub a resource and instead assert against the actual resource. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Learning&amp;lt;/strong&amp;gt; Limited time allocation helps with decisions about when to stop working on a solution and go back to rethink the approach.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;What’s next?&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;There are two directions where I could spend my time: &amp;lt;/p&amp;gt;

&amp;lt;ol&amp;gt;
&amp;lt;li&amp;gt;Expand the currently proposed courses and write the proposed list of emails/chapters I plan to tackle for each of them. &amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Implement a prototype for writing and sending an email to users. &amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;

&amp;lt;p&amp;gt;I am confident I can find a solution for 2, so maybe I should focus on the content part because people might be buying the course because of the content and not the platform. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Thus, I want to do a cycle focusing on the content and trying to expand it a bit more. &amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-23</pubDate>
      <link>https://learn.shortruby.com/blog/end-of-cycle1-retrospective</link>
      <guid>https://learn.shortruby.com/blog/end-of-cycle1-retrospective</guid>
    </item>
    <item>
      <title>First commits</title>
      <description>&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Image header with article title and screenshot of Github commits&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/first-commits-in-my-rails-repository.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I had the idea for this project over three months ago and created the Ruby on Rails repo around that time. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I was unsure what form the product would have, so I focused first on setting up coding styles, static analysis checks, and some other defaults that I consider essential when starting a project. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;In this article, I will explore what I added and why I chose that specific tool, talking specifically from the perspective of a single-person side project. &amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;What I wanted to achieve with my first commits&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;There is a kind of inertia to the code design: new code tends to be similar to existing code. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here is a better quote from Edmond Lau book called “The Effective Engineer” that shows the positive side of this inertia: &amp;lt;/p&amp;gt;

&amp;lt;blockquote&amp;gt;
&amp;lt;p&amp;gt;The code quality also self-propagates; new engineers model their own code based on the excellent code that they see, creating a positive feedback loop. &amp;lt;/p&amp;gt;
&amp;lt;/blockquote&amp;gt;

&amp;lt;p&amp;gt;When working alone on your project, you can replace “new engineer” with “you in the future,” the quote will still be relevant. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;My initial commits aimed to set up automated checks for code quality and security. I want to help my future self be fast and write quality code. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Side projects developed while having a full-time job have a unique characteristic worth noting. The time dedicated to working on the side project is not continuous. For instance, you may work on it for 1-2 hours on Saturday, and the next opportunity to work on it may only arise a week later.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;It is then essential to make the code quality built-in and use as much automation as possible. &amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;First commits&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;My first commits were about: &amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;adding Rubocop to correct and enforce coding styles automatically &amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;check code quality with Rubycritic&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;security audits with brakeman, bundler-audit and importmap audit&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;enable Rails strict loading&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;make Rails console run on sandbox mode in production&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;h3&amp;gt;Add Rubocop&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I have a &amp;lt;a href=&amp;quot;https://github.com/lucianghinda/personal-ruby-style&amp;quot;&amp;gt;Rubocop configuration&amp;lt;/a&amp;gt; that I like to use in my side projects. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The first commit sets up Rubocop and adds my configuration to the &amp;lt;code&amp;gt;.rubocop.yml&amp;lt;/code&amp;gt; file.&amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;Why add Rubocop&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;I have a lot of thoughts about what cop I want enabled and what cop disabled, and sometimes I change my mind about some of them, but for this project, consistency is more important than a specific cop. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;It is essential to have a consistent style while coding. I also like to have the editor auto-format my code, so I chose Rubocop because I want to use the new Ruby syntax, and thus, I can switch on/off rules that will impede this. &amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;What I added to the commit&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;1. Rubocop gems&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I am using the following Rubocop gems: &amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-rails&amp;quot;&amp;gt;rubocop-rails&amp;lt;/a&amp;gt; - “Automatic Rails code style checking tool”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-performance&amp;quot;&amp;gt;rubocop-performance&amp;lt;/a&amp;gt; - “A collection of RuboCop cops to check for performance optimizations in Ruby code”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-minitest&amp;quot;&amp;gt;rubocop-minitest&amp;lt;/a&amp;gt; - “Automatic Minitest code style checking tool”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-capybara&amp;quot;&amp;gt;rubocop-capybara&amp;lt;/a&amp;gt; - “Code style checking for Capybara test files (RSpec, Cucumber, Minitest)”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/rubocop-rubycw&amp;quot;&amp;gt;rubocop-rubycw&amp;lt;/a&amp;gt; -  “Integrate RuboCop and ruby -cw. You can get Ruby&amp;rsquo;s warning as a RuboCop offense by rubocop-rubycw”&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;2. Apply Rubocop autocorrection to files generated by &amp;lt;code&amp;gt;Rails generate&amp;lt;/code&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I want to automatically run Rubocop with autocorrect on any &amp;lt;code&amp;gt;.rb&amp;lt;/code&amp;gt; file generated by the Rails generate command. This will ensure that any gem that generates Ruby files or any I generate will have the style automatically corrected. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I added the following code to the &amp;lt;code&amp;gt;config/application.rb&amp;lt;/code&amp;gt; to do this: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;config&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;generators&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;after_generate&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;do&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;files&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;parsable_files&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;files&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;filter&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;file&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;file&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;end_with?&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;.rb&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;unless&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;parsable_files&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;empty?&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;system&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;bundle exec rubocop -A --fail-level=E &amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;#{&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;parsable_files&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;shelljoin&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;exception: &amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;)&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;end&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;I took this code from &amp;lt;a href=&amp;quot;https://docs.rubocop.org/rubocop-rails/usage.html#rails-configuration-tip&amp;quot;&amp;gt;Rubocop Rails Configuration tip&amp;lt;/a&amp;gt; and you can see it added to &amp;lt;a href=&amp;quot;https://github.com/rails/rails/blob/main/railties/lib/rails/configuration.rb#L134&amp;quot;&amp;gt;the current Rails master&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;And I made Rubocop run on Github CI with by adding the following content to the file under &amp;lt;code&amp;gt;.github/workflows/rubocop.yml&amp;lt;/code&amp;gt;: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;yml&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Rubocop Linter&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;on&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;push&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;branches&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;main&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;pull_request&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;branches&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;main&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;jobs&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;build&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;runs-on&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;ubuntu-latest&amp;lt;/span&amp;gt;

    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Checkout code&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;uses&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;actions/checkout@v2&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Set up Ruby&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;uses&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;ruby/setup-ruby@v1&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;with&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;ruby-version&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;3.3&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;bundler-cache&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Install dependencies&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle install&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Checking Rubocop Styles&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle exec rubocop&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;This will run Rubocop on the following two cases: 
- When code is pushed to the main branch.
- When a pull request is made to the main branch.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Code Quality with Rubycritic&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;The third commit adds &amp;lt;a href=&amp;quot;https://github.com/whitesmith/rubycritic&amp;quot;&amp;gt;Rubycritic&amp;lt;/a&amp;gt; as a code quality static analysis. &amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;Why add Rubycritic&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;I added this because I want to ensure I create simple code. And while Rubycritic does not guarantee the code will be simple, it can help maintainability. I see maintainability as an essential metric in the context of a side project. &amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;The commit&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;I don’t want to run Rubycritic in CI mostly because I think about having little time and trying to be fast with feature releases, and sometimes refactoring to remove a code smell can take quite some time. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;But I do want to run this locally, so I just created a file under &amp;lt;code&amp;gt;bin/quality_check&amp;lt;/code&amp;gt; with the following content: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;bash&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;#!/bin/bash -e&amp;lt;/span&amp;gt;

bundle &amp;lt;span style=&amp;quot;color: #f6aa11&amp;quot;&amp;gt;exec &amp;lt;/span&amp;gt;rubycritic 
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;Why a file and not run directly &amp;lt;code&amp;gt;rubycritic&amp;lt;/code&amp;gt;? I’d like to add more tools like this here or configure Rubycritic in a specific way and having a bash script allows me this flexibility.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Rubycritic uses &amp;lt;a href=&amp;quot;https://github.com/troessner/reek&amp;quot;&amp;gt;reek&amp;lt;/a&amp;gt; under the hood so I added a reek config files at &amp;lt;code&amp;gt;.reek.yml&amp;lt;/code&amp;gt; with the following content: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;yml&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;---&amp;lt;/span&amp;gt;
&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;detectors&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;IrresponsibleModule&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;enabled&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;false&amp;lt;/span&amp;gt;

  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;LongParameterList&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;enabled&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;exclude&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;[]&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;max_params&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;overrides&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;initialize&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;max_params&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;5&amp;lt;/span&amp;gt;

  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;DataClump&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;max_copies&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;min_clump_size&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;3&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;### Excluding directories&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;exclude_paths&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;test/&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;config/&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;I mostly disabled &amp;lt;code&amp;gt;IrresponsibleModule&amp;lt;/code&amp;gt; because I don’t want to add a description to all classes/modules I create. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I also allowed a maximum of 4 parameters for methods and 5 for the initializers. I also allowed a maximum of 3 methods in an object to have the same parameters, and the check happens only from 3 parameters up. I can rationalize this decision, but it is mainly based on my experience with extracting objects from a long list of parameters and finding a balance when to make this effort. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Security checks on CI&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;The fourth commit is concerned with adding security checks to the CI. &amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;Why add security checks&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;I want to run on CI a couple of security audits that I grouped under the name of &amp;lt;code&amp;gt;Static Analysis&amp;lt;/code&amp;gt;: &amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/brakeman&amp;quot;&amp;gt;Brakeman&amp;lt;/a&amp;gt; - “Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://rubygems.org/gems/bundle-audit&amp;quot;&amp;gt;Bundle audit&amp;lt;/a&amp;gt; - “Patch-level verification for Bundler”&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;https://github.com/rails/importmap-rails&amp;quot;&amp;gt;Importmap audit&amp;lt;/a&amp;gt; - “checks the NPM registry for known security issues”&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;h4&amp;gt;The commit&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;Here is the CI config for Github to run all these tools in &amp;lt;code&amp;gt;.github/workflows/static_analysis.yml&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;yml&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Static Analysis&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;on&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;push&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;branches&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;main&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;pull_request&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;branches&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;main&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;jobs&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
  &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;static_analysis&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;runs-on&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;ubuntu-latest&amp;lt;/span&amp;gt;

    &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Checkout code&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;uses&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;actions/checkout@v2&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Setup Ruby&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;uses&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;ruby/setup-ruby@v1&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;with&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;ruby-version&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ae81ff&amp;quot;&amp;gt;3.3&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;bundler-cache&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Install Bundler&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;gem install bundler&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Install dependencies&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;|&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle config path vendor/bundle&amp;lt;/span&amp;gt;
          &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle install --jobs 4 --retry 3&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Run bundler-integrity check&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle exec bundler-integrity&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Run Brakeman&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle exec brakeman&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Run Bundle Audit&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle exec bundle-audit check --update&amp;lt;/span&amp;gt;

      &amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;Run Importmap audit&amp;lt;/span&amp;gt;
        &amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;run&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;bundle exec bin/importmap audit&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;h3&amp;gt;Enable Rails strict loading&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Here is what &amp;lt;code&amp;gt;strict_loading&amp;lt;/code&amp;gt; does (&amp;lt;a href=&amp;quot;https://github.com/rails/rails/pull/37400&amp;quot;&amp;gt;source&amp;lt;/a&amp;gt;): &amp;lt;/p&amp;gt;

&amp;lt;blockquote&amp;gt;
&amp;lt;p&amp;gt;Add #strict to any record to prevent lazy loading of associations. strict will cascade down from the parent record to all the associations to help you catch any places where you may want to use preload instead of lazy loading.&amp;lt;/p&amp;gt;
&amp;lt;/blockquote&amp;gt;

&amp;lt;h4&amp;gt;Why enable strict loading&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;This is an excellent way to prevent N+1 and ensure that any needed association is included or preloaded. &amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;The commit&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;I enabled &amp;lt;code&amp;gt;strict_loading&amp;lt;/code&amp;gt; in all environments: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# config/environments/development.rb&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;config&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;active_record&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;strict_loading_by_default&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# config/environments/test.rb&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;config&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;active_record&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;strict_loading_by_default&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# config/environments/production.rb&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;config&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;active_record&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;strict_loading_by_default&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;h3&amp;gt;Make Rails console run on sandbox mode in production&amp;lt;/h3&amp;gt;

&amp;lt;h4&amp;gt;Why make console run in sandbox mode&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;It is a good practice to run the rails console in production in the sandbox box to ensure any change is intentional. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I use &amp;lt;code&amp;gt;rails console&amp;lt;/code&amp;gt; quite often in production, but I want to make sure that if I want to make any changes, that should be intentional and not accidental.&amp;lt;/p&amp;gt;

&amp;lt;h4&amp;gt;The commit&amp;lt;/h4&amp;gt;

&amp;lt;p&amp;gt;This is a &amp;lt;a href=&amp;quot;https://github.com/rails/rails/pull/48984&amp;quot;&amp;gt;simple switch&amp;lt;/a&amp;gt; that I can toggle in the Rails config for production: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #75715e&amp;quot;&amp;gt;# config/environments/production.rb&amp;lt;/span&amp;gt;

&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;config&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;sandbox_by_default&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;true&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;If this is enabled, then to make changes, you have to run &amp;lt;code&amp;gt;rails console --no-sandbox&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Conclusion&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;I made the first commits in this repository intentional about code quality and automatic checks. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;It helps with moving fast while keeping a good code quality. A lot can be done in this area, but I tried to add the minimum that would help me. &amp;lt;/p&amp;gt;

&amp;lt;hr&amp;gt;

&amp;lt;h3&amp;gt;A note about eager loading code&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;A previous version of this article included a section about configuring CI to run &amp;lt;code&amp;gt;bin/rails zeitwerk:check&amp;lt;/code&amp;gt; to check if the code loads correctly. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;a href=&amp;quot;https://hashref.com&amp;quot;&amp;gt;Xavier Noria&amp;lt;/a&amp;gt; correctly pointed out that running this in CI is unnecessary. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;First, there is a config setting called &amp;lt;a href=&amp;quot;https://guides.rubyonrails.org/v7.0/configuring.html#config-eager-load&amp;quot;&amp;gt;&amp;lt;code&amp;gt;config.eager_load&amp;lt;/code&amp;gt;&amp;lt;/a&amp;gt;, and if you put that on &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; in your test environment and run any test that will eagerly load your entire application. This setting exists already in Rails 6.0 (and even before 6.0). &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Since Rails 7.0 the generated &amp;lt;code&amp;gt;config/environment/test.rb&amp;lt;/code&amp;gt; includes the following line: &amp;lt;/p&amp;gt;
&amp;lt;pre class=&amp;quot;ruby&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;config&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;eager_load&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #f92672&amp;quot;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span style=&amp;quot;color: #66d9ef&amp;quot;&amp;gt;ENV&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #e6db74&amp;quot;&amp;gt;&amp;quot;CI&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #ffffff;background-color: #272822&amp;quot;&amp;gt;].&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color: #a6e22e&amp;quot;&amp;gt;present?&amp;lt;/span&amp;gt;
&amp;lt;/pre&amp;gt;
&amp;lt;p&amp;gt;That means if you run your tests in Github CI (or similar CIs) that sets the &amp;lt;code&amp;gt;CI&amp;lt;/code&amp;gt; environment variable, it will eagerly load your application. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;So there is no need to execute &amp;lt;code&amp;gt;rails zeitwerk:check&amp;lt;/code&amp;gt; separately. Either set the &amp;lt;code&amp;gt;config.eager_load&amp;lt;/code&amp;gt; to true on CI, or you already have that if you generated your app with Rails &amp;gt;= 7.0 and run your tests on CI. &amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-16</pubDate>
      <link>https://learn.shortruby.com/blog/first-commits</link>
      <guid>https://learn.shortruby.com/blog/first-commits</guid>
    </item>
    <item>
      <title>Early Launch</title>
      <description>&amp;lt;p&amp;gt;When is an excellent time to launch a project? &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Some people advise launching as early as possible. Just have a domain (or not), create a simple page where you explain the project, publish, and start talking about it.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Others advise waiting until you have some prototype or MVP or a tiny utility or value you can offer. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I think context also matters:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;What kind of product or content do you have?&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Is this part of an established brand, or are you launching a new brand?&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Is this a side project or part of an existing company?&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;The audience that you are trying to reach?&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;h2&amp;gt;Why launch early?&amp;lt;/h2&amp;gt;

&amp;lt;h3&amp;gt;Write about the journey while building the project&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;In my case, this project is a side project, and my idea is to launch it as early as possible because I plan to write about my journey while building it. To be part of the &amp;lt;a href=&amp;quot;https://buildinpublic.com&amp;quot;&amp;gt;#buildinpublic&amp;lt;/a&amp;gt; movement. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I liked following some people sharing their stories as they built their side projects. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Writing about technical or product problems as I encountered them is valuable, and this is what I plan to do. First, it helps me clear my thinking and simplify, adjust, and find solutions; second, it might inspire others who may have problems or explore similar paths. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Early feedback&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Launching early helps me get early feedback. That is a very valuable input to have when building a product. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Also, the launch of a project can alleviate the pressure of having a perfectly polished release. &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;This allows me to concentrate on small iterations and, when paired with writing and early feedback, enables me to release early and iterate instead of waiting for a significant feature to be completed and guess what possible users might want. &amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Current project status&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;The current status of the project: Building the platform and the &amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Thus, today is a good day to launch this.
As good as any other day!&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Wish me luck!&amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-10</pubDate>
      <link>https://learn.shortruby.com/blog/early-launch</link>
      <guid>https://learn.shortruby.com/blog/early-launch</guid>
    </item>
    <item>
      <title>Cycle 1 - Building a welcoming place</title>
      <description>&amp;lt;h2&amp;gt;Context&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;I have limited time to invest in building the app and making the content. Probably a couple of hours per week.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Thus, it is essential to be very strict with what I build and where to put my effort.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;So I am going to maintain a list of things that I work on for this project and I will group them under a theme or cycle.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Cycle 1: Building a welcoming place&amp;lt;/h2&amp;gt;

&amp;lt;h3&amp;gt;The theme&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;The central theme of this first cycle is to make the website &amp;lt;a href=&amp;quot;https://learn.shortruby.com&amp;quot;&amp;gt;learn.shortruby.com&amp;lt;/a&amp;gt; a welcoming place for visitors.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;There will be two metrics that I will watch for this:&amp;lt;/p&amp;gt;

&amp;lt;ol&amp;gt;
&amp;lt;li&amp;gt;PageSpeed insights (looking at performance, accessibility and SEO)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Number of visitors&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;

&amp;lt;p&amp;gt;The focus of this cycle will be on improving PageSpeed Insights.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Here is the current report: &amp;lt;a href=&amp;quot;https://pagespeed.web.dev/analysis/https-learn-shortruby-com/9kxackelgp&amp;quot;&amp;gt;link&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Image showing mobile pagespeed report&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/mobile-pagespeed-insights.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Image showing desktop pagespeed report&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/desktop-pagespeed-insights.webp&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Small projects&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Here is a list of small projects to achieve the goal of this cycle.&amp;lt;/p&amp;gt;

&amp;lt;ol&amp;gt;
&amp;lt;li&amp;gt;Add RSS feed for the blog&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;SEO: Add &amp;lt;a href=&amp;quot;https://github.com/kpumuk/meta-tags&amp;quot;&amp;gt;meta-tags&amp;lt;/a&amp;gt; gem to the app - see as the source of inspiration &amp;lt;a href=&amp;quot;https://github.com/marcoroth/hotwire.io&amp;quot;&amp;gt;Hotwire.io source code&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;SEO: Transform images to webp format&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;SEO: Fix issues from PageSpeed report&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Accessibility: Fix issues from PageSpeed report&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Feature: Add subscribe to get course updates - when they launch, pricing &amp;hellip;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Remove the empty state buttons: Buy Now and See More. Replace them with CTA to subscribe when the course is launched.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Add automatic Sitemap generation&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;

&amp;lt;h3&amp;gt;Decisions to make&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;I have to decide the following:&amp;lt;/p&amp;gt;

&amp;lt;ol&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;How do I manage the list of people that want to know when I launch? Is this a good time to list more general subscribers so that I can share what I work on and not be specific to a product?&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;What analytics to add to the website? So far, I have used Plausible for all my websites. I like it and there is no reason to change. I still ask myself the question: Is there something else to add? Should I spend time to look around?&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;

&amp;lt;p&amp;gt;That’s it for now. Let&amp;rsquo;s build and share!&amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-04</pubDate>
      <link>https://learn.shortruby.com/blog/cycle1-building-an-welcoming-place</link>
      <guid>https://learn.shortruby.com/blog/cycle1-building-an-welcoming-place</guid>
    </item>
    <item>
      <title>The initial pitch for an email course</title>
      <description>&amp;lt;p&amp;gt;Here is why I decided to build this platform and some high-level plans of what I am trying to do with it.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;The problem&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;Staying up to date with the evolution of technology is among the core tasks of a software developer. There are multiple ways to achieve this: reading social media, subscribing to a newsletter, watching videos, reading articles, or reading books.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Graph showing current learning platforms&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/email-courses-fit.png&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;There is a missing product that should fit into the mix of deep dive with less effort than reading a book.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;The solution&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;For lack of a better word, I will call this solution an email course.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;A series of emails are sent periodically. Usually, they are sent every week, but the user can choose to receive them more often.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;There can be other media to share the knowledge this way - split in chunks and consumed over time. My proposal is email because it is a means of communication that works well but leaves the reader&amp;rsquo;s attention and decision. It does not need to be read now; the reader can choose when and how to read it.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The same can, of course, be said about a series of articles or YouTube videos. They all have their purpose and fit well within a diverse group. I like email because it is a two-way communication: it can be read, but I can reply quickly. It creates a communication channel. But I also like it because it is text, and text is an amazing way to express ideas and to discover new concepts.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Any email course should be able to be restarted as many times as possible.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Novelty&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;This idea is not new. This idea has various forms - some more similar and some a bit different.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I plan to focus on making it the best learning experience and, in the case of the content, to focus on Ruby and Ruby on Rails content. I will explore this as I build the platform and write the content.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;An example of an email course&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;Here is one example of how I would split an email course into topics:&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Example of email course about new features in Ruby&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/new-features-in-ruby.png&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Of course, the actual content of each week might change based on the feedback and what I discovered while creating each week&amp;rsquo;s email.&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;The interface&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;As the primary medium is email, the interface should allow passwordless login via email.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The user should be able to see the running and finished courses and restart any of them at any point. At the end of a course, there will be the option to download a PDF version containing the same content as the course.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;img alt=&amp;quot;Example of how the welcome screen might looks like&amp;quot; src=&amp;quot;https://images.learn.shortruby.com/welcome-screen.png&amp;quot; /&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;h3&amp;gt;Composing emails&amp;lt;/h3&amp;gt;

&amp;lt;p&amp;gt;For composing emails, I will either use the default editor from Rails and send emails as they are formatted by Trix or compose the emails in any other email delivery service and then, from the app, trigger the end of that email from there. I will these two solutions and pick one that first best the initial workflow.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;No Gos&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;I will not build my email editor/composer.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I will use an email delivery service - probably Postmark - and will only try implementing some email delivery-related tasks myself.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The focus will be on the content and providing a simple way to configure when the emails will be sent.&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Conclusion&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;This is the initial pitch about what I want to create and why I want to create it.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I will try to post as often as possible about how I think about building this product.&amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-03</pubDate>
      <link>https://learn.shortruby.com/blog/the-initial-pitch-for-an-email-course</link>
      <guid>https://learn.shortruby.com/blog/the-initial-pitch-for-an-email-course</guid>
    </item>
    <item>
      <title>What I plan to write</title>
      <description>&amp;lt;p&amp;gt;Hello world!&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I noticed over the years that writing things down helps me gain clarity and focus.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;So, I am launching the blog section of this product, even if the product still needs to be built, as an exercise for me to write down my thoughts as I build this learning product.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The content that you will discover here will be a mix of:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;product decisions and explorations&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;technical assessments&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;technical decisions&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;retrospectives&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;sharing the progress&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;I plan to be as open as possible and share my thoughts as I create this product in the spirit of #buildinpublic.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;That’s it for now!&amp;lt;/p&amp;gt;
</description>
      <pubDate>2024-01-02</pubDate>
      <link>https://learn.shortruby.com/blog/what-i-plan-to-write</link>
      <guid>https://learn.shortruby.com/blog/what-i-plan-to-write</guid>
    </item>
  </channel>
</rss>
