<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jared Norman</title>
    <description>The latest articles on Forem by Jared Norman (@supergoodjared).</description>
    <link>https://forem.com/supergoodjared</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F11675%2Feedcf41f-4a48-4a73-ac83-45dd03411fea.jpg</url>
      <title>Forem: Jared Norman</title>
      <link>https://forem.com/supergoodjared</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/supergoodjared"/>
    <language>en</language>
    <item>
      <title>Anonymous Struct Literals Might Be Coming To Ruby</title>
      <dc:creator>Jared Norman</dc:creator>
      <pubDate>Tue, 07 Jul 2020 15:38:44 +0000</pubDate>
      <link>https://forem.com/supergoodsoft/anonymous-struct-literals-might-be-coming-to-ruby-j84</link>
      <guid>https://forem.com/supergoodsoft/anonymous-struct-literals-might-be-coming-to-ruby-j84</guid>
      <description>&lt;p&gt;There's been some really interesting discussion in the Ruby community about adding anonymous struct literals to the language. My Japanese isn't very good (I don't speak Japanese), but Koichi Sasada and Matz had a little Twitter exchange about the idea and that's now turned into &lt;a href="https://bugs.ruby-lang.org/issues/16986"&gt;a proposal&lt;/a&gt; on the Ruby tracker where there's been further discussion.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o8wi9oUJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/EbV1Z9VUwAII7MK.png" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--70KBapKb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1146121561/QR_Code_normal.jpg" alt="_ko1 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        _ko1
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @_ko1
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/yukihiro_matz"&gt;@yukihiro_matz&lt;/a&gt; just idea なんですが、無名Structを簡単に作る仕組みをいれるのはどうでしょうか。同じキーセットなら、同じ無名 Struct が返るような感じで。 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      07:30 AM - 25 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1276055259241046016" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1276055259241046016" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      3
      &lt;a href="https://twitter.com/intent/like?tweet_id=1276055259241046016" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      14
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  What's a Struct?
&lt;/h2&gt;

&lt;p&gt;Ruby provides &lt;a href="https://ruby-doc.org/core-2.7.1/Struct.html"&gt;Structs&lt;/a&gt; as a convenient way to create classes that group together a set of fields without forcing you to explicitly define a class with all the accessor methods.&lt;/p&gt;

&lt;p&gt;For example, if you needed to pass around a representation of a dog but didn't need any additional behaviour you might do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:breed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;struct Dog name="Roxie", breed="whippet-cross", age=4&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Roxie"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;breed&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;struct Dog name="Roxie", breed="whippet-cross", age=5&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;roxie2&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Because instances of Structs are equal if their attributes are all equal, it makes them a great solution for defining &lt;a href="https://martinfowler.com/bliki/ValueObject.html"&gt;value objects&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the proposal add?
&lt;/h2&gt;

&lt;p&gt;There's some disagreement on the syntax, but the idea is to add a syntax for defining anonymous instances of Structs in-line. Today, you might write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:breed&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this proposal you'd be able to simply write something like this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;breed: &lt;/span&gt;&lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This would create an instance of an anonymous Struct class without having to explicitly define and instantiate the class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is that useful?
&lt;/h2&gt;

&lt;p&gt;There are actually a number of benefits to this, but let's start with why anonymous Structs are useful regardless of the syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structs avoid typos
&lt;/h3&gt;

&lt;p&gt;In Ruby we often use hashes for passing around things like configuration options and other structured data. Structs allow us to do this without allowing arbitrary properties.&lt;/p&gt;

&lt;p&gt;In our dog example above, &lt;code&gt;roxie.breeed = "pug"&lt;/code&gt; will cause an error because we spelled "breed" incorrectly and the Struct doesn't have a member called "breeed". If were using a Hash instead then we'd be able to accidentally assign &lt;code&gt;roxie[:breeed] = "pug"&lt;/code&gt; without any errors, accidentally creating a new typoed key in the Hash.&lt;/p&gt;

&lt;p&gt;There are many cases where you need arbitrary properties, but for those cases you can use OpenStruct or a Hash. When accepting configuration from a user or any other situation where you know the possible fields in advance Structs work great.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anonymous Structs communicate intent
&lt;/h3&gt;

&lt;p&gt;Writing code that communicates its own intent clearly is useful for helping future developers understand and modify that code. Using an anonymous Struct rather than a Hash or OpenStruct communicates to the person reading the code that the fields the object is defined with are the &lt;em&gt;only&lt;/em&gt; fields that object supports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structs are simpler to access
&lt;/h3&gt;

&lt;p&gt;This is admittedly a small improvement, but it's simpler to type &lt;code&gt;roxie.breed&lt;/code&gt; than &lt;code&gt;roxie[:breed]&lt;/code&gt;. In this example &lt;code&gt;roxie&lt;/code&gt; doesn't represent an arbitrary mapping of keys to values, but a dog, so if we're following conventional object-oriented design practices, then &lt;code&gt;roxie&lt;/code&gt; should be an object that responds to messages about itself.&lt;/p&gt;

&lt;p&gt;Using Structs over Hashes also means that refactoring the code to use a real Class won't mean you need to change everywhere its data is accessed.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenStruct is slow
&lt;/h3&gt;

&lt;p&gt;OpenStruct is another class in the standard library that provides the ability to create objects that are basically like Structs, but are "open" to creating new attributes like Hashes are.&lt;/p&gt;

&lt;p&gt;This is extremely useful if you need to support arbitrary attributes on the object, but if you don't you're just opening yourself up to the same issue as using a Hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;OpenStruct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"Roxie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;breed: &lt;/span&gt;&lt;span class="s2"&gt;"whippet-cross"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;OpenStruct name="Roxie", breed="whippet-cross"&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;breeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"french bulldog"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"french bulldog"&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;pry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roxie&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;OpenStruct name="Roxie", breed="whippet-cross", breeed="french bulldog"&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Additionally, while OpenStructs are definitely great when you need their "open" functionality, they are much slower than regular Structs. Koichi Sasada &lt;a href="https://bugs.ruby-lang.org/issues/16986#Performance"&gt;notes this&lt;/a&gt; in the performance section of his proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  But it says in the proposal that Hashes are faster!
&lt;/h2&gt;

&lt;p&gt;One interesting detail that the proposal brings up is that Hashes are currently faster than Structs for this kind of usage. This is true, but Hashes have seen plenty of optimization because of how heavily we rely on them.&lt;/p&gt;

&lt;p&gt;You would think that Structs would be faster than Hashes since their behaviour is simpler. Well, that's not the case but Koichi Sasada is confident that it &lt;em&gt;could&lt;/em&gt; be the case with some optimization.&lt;/p&gt;

&lt;p&gt;This proposal also enables a whole new optimization that is only briefly mentioned:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks to this spec, we can specify anonymous Struct classes at compile time. We don't need to find or create Struct classes at runtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the attributes of the Structs is known at &lt;em&gt;parse&lt;/em&gt; time rather than &lt;em&gt;run&lt;/em&gt; time, it is possible to define the anonymous classes at compile time so that using this syntax incurs only the overhead of instantiating the anonymous class without having to define it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it mean for you?
&lt;/h2&gt;

&lt;p&gt;For now, not much. We can't use this in our programs yet, but we are free to use anonymous Structs in our code without the special syntax. If you're creating value objects, want to limit the attributes on your object, or just want to make refactoring easier, this isn't a bad idea.&lt;/p&gt;

&lt;p&gt;That said, keep an eye on this proposal. Switching to this syntax in your applications when it becomes available could have performance benefits, save on typos, and make your code clearer.&lt;/p&gt;

&lt;p&gt;What do you think about this new syntax? Let me know &lt;a href="https://twitter.com/SuperGoodJared/status/1280509476861288449"&gt;on Twitter!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Working Around ActiveRecord Callbacks</title>
      <dc:creator>Jared Norman</dc:creator>
      <pubDate>Tue, 10 Dec 2019 16:05:10 +0000</pubDate>
      <link>https://forem.com/supergoodsoft/working-around-activerecord-callbacks-pj0</link>
      <guid>https://forem.com/supergoodsoft/working-around-activerecord-callbacks-pj0</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was original posted &lt;a href="https://supergood.software/working-around-activerecord-callbacks/"&gt;over here on the Super Good Software website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Working on applications that overuse ActiveRecord callbacks can be painful. Saving or updating any given record might cause a cascade of API calls and business logic that's totally irrelevant to what you're trying accomplish. I've got a great trick for working around troublesome callbacks by allowing you to easily prevent them from running as needed.&lt;/p&gt;

&lt;p&gt;I strongly believe that business logic shouldn't be implemented using ActiveRecord callbacks. Callbacks are great for data normalization and caching computed values. Sending e-mails, making API calls, and other side-effects should be implemented using some other programming pattern that untangles the logic from your persistence layer. Excessive use of callbacks leads to slow test suites, brittle systems, and unintended changes.&lt;/p&gt;

&lt;p&gt;I run into many applications with complex third-party API integrations that are fueled by these cascades of &lt;code&gt;after_save&lt;/code&gt;, &lt;code&gt;after_create&lt;/code&gt;, and &lt;code&gt;after_commit&lt;/code&gt; callbacks and are difficult to understand and debug. Ideally I'd love to untangle these messes and pull out the business logic into classes that can easily be understood and tested separately from the persistence layer, but I don't always have the time to do that.&lt;/p&gt;

&lt;p&gt;Imagine this situation: you've got an &lt;code&gt;Address&lt;/code&gt; model with a &lt;code&gt;before_save&lt;/code&gt; callback that fetches and sets the latitude and longitude for that address. You're on a deadline and don't have the time to refactor every location in the code where you create an address. The callback is also slowing down your test suite because it's making slow requests out to the geolocation API every time you create an address. What's more, you've found some locations in the app where you create addresses and already know the coordinates. In these scenarios you don't need to do the lookup, but the lookup is being performed anyway.&lt;/p&gt;

&lt;p&gt;You could address the test suite speed issue by using something like VCR, but with a few hundred tests creating addresses, that'll generate &lt;em&gt;a ton&lt;/em&gt; of cassettes and doesn't solve the problem of the unnecessary API calls anyway.&lt;/p&gt;

&lt;p&gt;There's a solution that alleviates both these problems with minimal effort. Let's say our address class looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;
  &lt;span class="n"&gt;before_save&lt;/span&gt; &lt;span class="ss"&gt;:set_geolocation&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_geolocation&lt;/span&gt;
    &lt;span class="c1"&gt;# Hit some API or something...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What we can do is add an &lt;code&gt;attr_accessor&lt;/code&gt; to control whether we want to perform geolocation and then condition the callback on that attribute. (&lt;code&gt;attr_accessor :disable_geolocation&lt;/code&gt; is a handy shorthand for defining a &lt;code&gt;disable_geolocation&lt;/code&gt; reader method and a &lt;code&gt;disable_geolocation=&lt;/code&gt; writer method.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:disable_geolocation&lt;/span&gt;
  &lt;span class="n"&gt;before_save&lt;/span&gt; &lt;span class="ss"&gt;:set_geolation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;unless: :disable_geolocation&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_geolocation&lt;/span&gt;
    &lt;span class="c1"&gt;# Hit some API or something...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now when updating or creating our addresses we can pass in this attribute to control whether geolocation is performed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This one will still cause geolocation:&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;line1: &lt;/span&gt;&lt;span class="s2"&gt;"910 Government St"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;city: &lt;/span&gt;&lt;span class="s2"&gt;"Victoria"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;province: &lt;/span&gt;&lt;span class="s2"&gt;"British Columbia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;country: &lt;/span&gt;&lt;span class="s2"&gt;"Canada"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Here we prevent geolocation from running:&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;disable_geolocation: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;line1: &lt;/span&gt;&lt;span class="s2"&gt;"1328 Douglas St"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This works because ActiveRecord methods like &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;create&lt;/code&gt; basically just assign the values you pass in, so it doesn't matter that &lt;code&gt;disable_geolocation&lt;/code&gt; isn't backed by a database column. This also means that you can update your factory definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Jardo Namron"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:street_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Fake St."&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Vancouver"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;province&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"British Columbia"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Canada"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;disable_geolocation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When you create addresses using the factory you won't get geolocation by default, but can opt in as needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# No geolocation!&lt;/span&gt;
&lt;span class="n"&gt;address1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Yes geolocation!&lt;/span&gt;
&lt;span class="n"&gt;address2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;disable_geolocation: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This pattern comes in really handy when you don't have the time to make the larger architectural changes to remove the offending callbacks altogether. It's definitely a hack; externally controlling an object's behaviour like this is an antipattern by my standards, but it extends well to more complex situations and cleanly addresses the immediate problem, so I hope you find it useful.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>My 2020 Reading List</title>
      <dc:creator>Jared Norman</dc:creator>
      <pubDate>Sun, 08 Dec 2019 23:10:16 +0000</pubDate>
      <link>https://forem.com/supergoodjared/my-2020-reading-list-4ii1</link>
      <guid>https://forem.com/supergoodjared/my-2020-reading-list-4ii1</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally published on my &lt;a href="https://jarednorman.ca"&gt;personal site&lt;/a&gt;, &lt;a href="https://jarednorman.ca/my-2020-reading-list/"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At the beginning of 2019 I set out to read (or listen to) at least one book every month this year. It turned out that this was a poorly considered goal; I'd well exceeded that reading pace in 2018 and did so again in 2019. I don't have an exact count on the number of books I've finished this year, but I suspect it's around 30 (based on a quick glance at my Kindle/Audible libraries and bookshelf.)&lt;/p&gt;

&lt;p&gt;This coming year I've committed to do more actual reading rather than listening to audiobooks. Of the books I finished last year, I'm sure no more than 5 were physical or kindle books. I normally listen to audiobooks while running, weightlifting, doing chores, and just relaxing, so it's relatively easy for me to work through a couple per month.&lt;/p&gt;

&lt;p&gt;In 2020 I want to make better use of my reading time. I want to get more out of the books I read. To that end, my focus will be on continuing to make time for focused reading. I've already been doing a lot more this month due to the "digital declutter" I mentioned &lt;a href="https://jarednorman.ca/here-comes-2020/"&gt;in my last post&lt;/a&gt;; the reduced screen time has so far created quick a lot of extra time and I've spent a solid amount of it buried in books.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Looking Forward To
&lt;/h2&gt;

&lt;p&gt;I've reviewed my notebooks, my scribbled recommendations, and done some research of my own to compile a first draft of what I'd like to read in 2020. Here are a few titles I'm particularly looking forward to reading for the first time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Bell Jar&lt;/strong&gt; by &lt;em&gt;Sylvia Plath&lt;/em&gt;: I'm pretty ignorant when it comes to poetry, but I'm going to fix that in 2020. Before I dive into her poetry, I plan to read Plath's semi-autobiographical novel, The Bell Jar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On Writing Well&lt;/strong&gt; by &lt;em&gt;William Zinsser&lt;/em&gt;: I like to think of myself as being reasonably articulate in writing, but would love to improve. I really enjoy writing and haven't put any serious effort in improving at it (other than just writing blog posts) in more than a decade. I hope this will reignite some of my interest in the practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting to Unit Test&lt;/strong&gt; by &lt;em&gt;Erik Dietrich&lt;/em&gt;: Unit testing is pretty second nature to me, so I'm going to look back to the fundamentals this year to see what I've missed along the way. I'm sure there are gaps to fill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain-Driven Design: Tackling Complexity&lt;/strong&gt; by &lt;em&gt;Eric Evans&lt;/em&gt;: Domain-driven design is an approach I'm familiar with, but have never really dived into. This year I'm going to remedy that by reading this title as well as &lt;em&gt;Vaughn Vernon&lt;/em&gt;'s &lt;strong&gt;Implementing Domain-Driven Design&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compilers: Principles, Techniques, and Tools&lt;/strong&gt; by &lt;em&gt;Aho&lt;/em&gt;, &lt;em&gt;Sethi&lt;/em&gt;, and &lt;em&gt;Ullman&lt;/em&gt;: I've always wanted to read "The Dragon Book" and I'm hoping this year to work through it. I don't have a specific goal in reading this book, but I hope to fill in some blank spots left from having not taken the programming languages course when I was in university.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've also found a number of books in my collection in need of a second (or third, or fourth) pass. Here's a few that stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Working Effectively with Legacy Code&lt;/strong&gt; by &lt;em&gt;Michael C. Feathers&lt;/em&gt; and &lt;strong&gt;Growing Object-Oriented Software, Guided by Tests&lt;/strong&gt; by &lt;em&gt;Steve Freeman&lt;/em&gt; and &lt;em&gt;Nat Pryce&lt;/em&gt;: After I've finished &lt;strong&gt;Starting to Unit Test&lt;/strong&gt; I want to reread these two classics on unit testing. I know I've internalized many lessons I took away from them when I first read them, but it's definitely time to see what more I can get from them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The C Programming Language&lt;/strong&gt; by &lt;em&gt;Kernighan&lt;/em&gt; and &lt;em&gt;Ritchie&lt;/em&gt;: I actually own a first edition copy of this book, though it's in horrible shape. I accidentally ripped the front cover off while pulling it out of my bag, so it's now taped back together. I haven't seriously written any C in ages, but I have fond memories of reading this one as a teen and am excited to revisit it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Mythical Man-Month: Essays on Software Engineering&lt;/strong&gt;
by &lt;em&gt;Brooks&lt;/em&gt;: This should be an annual read for anyone who runs any kind of software organization. Aside: I'm sad that the giant sloths depicted on the cover aren't still roaming the earth. I've considered getting a tattoo of one because a) they are extremely cool and b) "adding manpower to a late software project makes it later".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sure it will grow and change over the course of the year, but you can find the full list of books on &lt;a href="https://www.goodreads.com/review/list/10814386-jared?shelf=to-read"&gt;my Goodreads account&lt;/a&gt; along with what I'm reading now.&lt;/p&gt;

&lt;p&gt;If you've got any book recommendations, please &lt;a href="https://twitter.com/SuperGoodJared"&gt;tweet at me!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8h90oftq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pbl1iscv28e2z15nzlja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8h90oftq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pbl1iscv28e2z15nzlja.png" alt="an image showing the covers of the books currently on my reading list for 2020"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Solidus Conf 2019 In Review</title>
      <dc:creator>Jared Norman</dc:creator>
      <pubDate>Thu, 31 Oct 2019 18:25:15 +0000</pubDate>
      <link>https://forem.com/solidusio/solidus-conf-2019-in-review-51ko</link>
      <guid>https://forem.com/solidusio/solidus-conf-2019-in-review-51ko</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally posted on the &lt;a href="https://supergood.software" rel="noopener noreferrer"&gt;Super Good&lt;/a&gt; website &lt;a href="https://supergood.software/solidus-conf-2019-in-review/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last week, the Solidus community gathered in Salt Lake City to discuss the future of the platform and ecosystem. Over the course of four days, we discussed where we're at, where we're headed, and laid the groundwork for moving the platform forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  The History of Solidus Conf
&lt;/h2&gt;

&lt;p&gt;Solidus Conf has existed in some form since the very beginning of the project. In the spring of 2015, developers from Stembolt and Bonobos disappeared into the forest near Whistler, BC and emerged after a week with a plan and a direction for the new platform.&lt;/p&gt;

&lt;p&gt;Every year since there's been another conference: 2016 we invaded Toronto, 2017 was across the pond in London, and last year we found ourselves talking Solidus over the best barbecue that Memphis has to offer.&lt;/p&gt;

&lt;p&gt;It's fair to say that this is a big year for the conference and the platform, though. Last year in Memphis it was announced that Stembolt had been acquired by Juul and would no longer be stewarding the platform in their previous capacity. This meant a lot of change was coming and while we did our best to plan for the future, it was really hard to understand what this might mean for the community.&lt;/p&gt;

&lt;p&gt;This year's conference took the same format as most previous years: two days of conference talks and two days of hack days. The presenters this year all did a fantastic job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day One
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ThomasSample" rel="noopener noreferrer"&gt;Thomas Sample&lt;/a&gt; of &lt;a href="https://karmacreative.io/" rel="noopener noreferrer"&gt;Karma Creative&lt;/a&gt; kicked things off, jamming more than one hundred SEO tips into a single talk. It was an awesome way to start the conference. Thomas does a ton for Solidus and his energy was a great way to start the conference.&lt;/p&gt;

&lt;p&gt;Next was one of my favourite talks; &lt;a href="https://twitter.com/cromwellryan" rel="noopener noreferrer"&gt;Ryan Cromwell&lt;/a&gt; of &lt;a href="https://seesparkbox.com/" rel="noopener noreferrer"&gt;Sparkbox&lt;/a&gt; spoke about "Making the Leap to Tech Lead". At Super Good, levelling up our developers is one of our main focuses and Ryan had a lot of great advice around organizing streams of work and making space for team members to grow.&lt;/p&gt;

&lt;p&gt;The third talk was focused on how consultants might improve our ability to gauge the quality of the codebases we're jumping into. &lt;a href="https://twitter.com/etagwerker" rel="noopener noreferrer"&gt;Ernesto Tagwerker&lt;/a&gt; from &lt;a href="https://www.ombulabs.com/" rel="noopener noreferrer"&gt;OmbuLabs&lt;/a&gt; presented on his new tool &lt;a href="https://github.com/fastruby/skunk" rel="noopener noreferrer"&gt;skunk&lt;/a&gt; which provides an integrated code smell/test coverage heuristic, so you can identify major code liabilies when stepping into a new project.&lt;/p&gt;

&lt;p&gt;After that, &lt;a href="https://github.com/skukx" rel="noopener noreferrer"&gt;Taylor Scott&lt;/a&gt; gave us a complete breakdown of the continuous integration and deployment process they use at &lt;a href="https://deseretbook.com" rel="noopener noreferrer"&gt;Deseret Book&lt;/a&gt;. Having run Solidus (and Rails) apps on a few of Amazon's different hosting offerings, it was interesting to see how they're leveraging &lt;a href="https://aws.amazon.com/elasticbeanstalk/" rel="noopener noreferrer"&gt;Elastic Beanstalk&lt;/a&gt; and &lt;a href="https://about.gitlab.com/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the final talk of the first day, &lt;a href="https://twitter.com/pberkenbosch" rel="noopener noreferrer"&gt;Peter Berkenbosch&lt;/a&gt; went over some of the lesser known extension points in Solidus, and some cool uses for them. A lot of work goes into making Solidus more extensible, so it was great for someone to help raise up some of the lesser used extension points.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnzgu23uzut7e91y0eqzi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnzgu23uzut7e91y0eqzi.jpg" alt="Ryan Cromwell addressing the Solidus Conf audience"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next For Solidus Extensions
&lt;/h2&gt;

&lt;p&gt;Before Peter's talk, we got a talk that deserves a little more attention. &lt;a href="https://alessandro.codes/" rel="noopener noreferrer"&gt;Alessandro Desantis&lt;/a&gt; from &lt;a href="https://nebulab.it/" rel="noopener noreferrer"&gt;Nebulab&lt;/a&gt; presented on the state, direction, and plan for the Solidus's many extensions.&lt;/p&gt;

&lt;p&gt;As I mentioned before, this was the first Solidus Conf since Stembolt stepped back from their role as the primary directors of the Solidus platform. As such, everyone was curious to see what Nebulab has been doing to continue to maintain and grow the platform. Alessandro stepped up to outline everything they were doing to keep our extension ecosystem healthy.&lt;/p&gt;

&lt;p&gt;Firstly, they're moving all the non-core extensions into the &lt;a href="https://github.com/solidusio-contrib" rel="noopener noreferrer"&gt;Solidus Contrib organization&lt;/a&gt;. The core team of Solidus is responsible for the extensions and main gems, now all housed under the &lt;a href="https://github.com/solidusio" rel="noopener noreferrer"&gt;main organization&lt;/a&gt;. The broader community is responsible for the maintenance of everything under the contrib organization.&lt;/p&gt;

&lt;p&gt;Before Stembolt was acquired by Juul Labs and merged into their development team, Stembolt had adopted an internal policy that all employees working at the company would be responsible for one or more Solidus extensions. This was to ensure that even lesser-used extensions had someone directly responsible for them who would respond to pull request and issues and keep them up to date.&lt;/p&gt;

&lt;p&gt;Nebulab has implemented the same policy. While they welcome support from the community in supports the extensions in Contrib, each extension has a Nebulab developer responsible for it's maintenance. While this policy was only somewhat successful at Stembolt, Nebulab has set themselves up for success with it. Nebulab dedicates Fridays to non-client work: writing blog posts, working on internal tools, and open source work. Time of this kind of open-source work is baked into their process and it's great to see that kind of commitment.&lt;/p&gt;

&lt;p&gt;On top of committing to keeping the ecosystem maintained, they've done some great work to make that easier for everyone. They've updated &lt;a href="https://github.com/solidusio-contrib/solidus_cmd" rel="noopener noreferrer"&gt;solidus_cmd&lt;/a&gt; to make various maintenance tasks easier and adopted a consistent README format for Solidus extensions to make it easier for those new to Solidus to understand what the extension ecosystem has for them.&lt;/p&gt;

&lt;p&gt;They're using &lt;a href="https://dependabot.com/" rel="noopener noreferrer"&gt;dependabot&lt;/a&gt; and &lt;a href="https://pullreminders.com/" rel="noopener noreferrer"&gt;Pull Reminders&lt;/a&gt; to stay on top of updates, as well as &lt;a href="https://github.com/github-changelog-generator/github-changelog-generator" rel="noopener noreferrer"&gt;GitHub Changelog Generator&lt;/a&gt; to make sure that it's as easy as possible to upgrade all the extensions your store is using.&lt;/p&gt;

&lt;p&gt;Finally, they're committed to addressing the issues we've talked about for a long time in the Solidus (and Spree) communities: &lt;code&gt;class_eval&lt;/code&gt; is bad and &lt;code&gt;Module#prepend&lt;/code&gt; isn't much better. The success of the extension ecosystem is contingent on becoming less dependent on these methods of modifying Solidus's core and providing the configuration options and extension points that real stores need. It's great to see them continuing that effort.&lt;/p&gt;

&lt;p&gt;I was really glad to see that some serious thought is going into how we're handling extensions and that Nebulab is doing their part to keep the ecosystem up to date. That said, they're only one member of this community and they need the assistance of the rest of us to make Solidus as awesome as it can be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day Two
&lt;/h2&gt;

&lt;p&gt;Day two featured talks from &lt;a href="https://www.joelsaupe.com/" rel="noopener noreferrer"&gt;Joel Saupe&lt;/a&gt; on the Solidus API, &lt;a href="https://twitter.com/knitcodemonkey" rel="noopener noreferrer"&gt;Jen Luker&lt;/a&gt; on how we can make sure not just our products, but our companies are accessible to everyone, &lt;a href="https://twitter.com/braidn" rel="noopener noreferrer"&gt;Braden Douglass&lt;/a&gt; on breaking up monoliths with microservices, and &lt;a href="https://www.codewithjason.com/" rel="noopener noreferrer"&gt;Jason Swett&lt;/a&gt; on how to confidently modify legacy code, something everyone work on Solidus has to do on a day-to-day basis.&lt;/p&gt;

&lt;p&gt;We also had a lightning talk section that provided some really great insight into what the community is working on. &lt;a href="https://eric.sau.pe/" rel="noopener noreferrer"&gt;Eric Saupe&lt;/a&gt; from the core team described the architecture of the point-of-sale system he's working on. &lt;a href="http://www.matthewbass.com/" rel="noopener noreferrer"&gt;Matthew Bass&lt;/a&gt; showed off an awesome shipping rate estimator that sidesteps the unreliable APIs they'd normally use, but still makes sure you're charging your customers the right amount.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Roadmap
&lt;/h2&gt;

&lt;p&gt;For the final lightning talk &lt;a href="https://github.com/gmacdougall" rel="noopener noreferrer"&gt;Gregor Macdougall&lt;/a&gt; of Juul Labs and &lt;a href="https://twitter.com/AlbertoVena" rel="noopener noreferrer"&gt;Alberto&lt;/a&gt; of Nebulab (both on the Solidus core team) outlined all the work that's gone into putting together a roadmap for the project.&lt;/p&gt;

&lt;p&gt;They went over the community survey results, reviewed independent market research, examined alternative eCommerce platforms, and used common sense to outline a laundry list of what Solidus needs to continue to grow.&lt;/p&gt;

&lt;p&gt;There are tons of stores on Solidus today, but the more stores we can bring to the platform, the more open source contributions we can leverage to make this a better platform for everyone.&lt;/p&gt;

&lt;p&gt;The roadmap is public and can be found &lt;a href="https://trello.com/b/RHPGLLy3/solidus-roadmap" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It's a work in progress, so there'll be more prioritization and consultation to come, but it represents the next steps for the Solidus platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Driven Solidus
&lt;/h2&gt;

&lt;p&gt;The final talk of the conference (and what a talk to end on) was from &lt;a href="https://twitter.com/Seanphden" rel="noopener noreferrer"&gt;Sean Denny&lt;/a&gt;. Sean is a member of the stakeholders team and did a ton of work to make this year's conference (and last years) happen.&lt;/p&gt;

&lt;p&gt;Sean started by outlining the history of the project and how the organization functions now, before diving into his thoughts on the next steps for Solidus. He's done a ton for this community, so when he talks about the need for greater diversity and calls on everyone to "take responsibility" for moving Solidus forward, it carries a lot of weight.&lt;/p&gt;

&lt;p&gt;I encourage everyone in the community to go give his talk a listen once it gets posted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hack Days
&lt;/h2&gt;

&lt;p&gt;Hack days are a staple of Solidus Conf. They help capture the energy and enthusiasm that builds around the conference and turn that into real contributions. Having everyone in the same room helps facilitate broader discussions and provides opportunities for newcomers to the platform to get involved.&lt;/p&gt;

&lt;p&gt;This year we saw everything from &lt;a href="https://github.com/solidusio/solidus/pull/3392" rel="noopener noreferrer"&gt;optimizations that leverage new Rails 6 features&lt;/a&gt; to some of our &lt;a href="https://github.com/solidusio/solidus/pull/3394" rel="noopener noreferrer"&gt;oldest issues closed&lt;/a&gt;. I spent my time getting the &lt;a href="https://github.com/jhawthorn/discard/" rel="noopener noreferrer"&gt;discard project&lt;/a&gt; cleaned up to enable us to &lt;a href="https://github.com/solidusio/solidus/issues/3393" rel="noopener noreferrer"&gt;complete the migration away from paranoia&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Stakeholders Meeting
&lt;/h2&gt;

&lt;p&gt;The Solidus stakeholders team meets weekly to manage the project, so this week we held our meeting at the hack days and made it open to anyone who was present. Everyone at the hack day was able to voice their opinion and get a look at how things are run.&lt;/p&gt;

&lt;p&gt;This facilitated discussions around the material in the roadmap, what might be missing, and how to best prioritize that. There'll be more to come on that as the stakeholders team continues to refine the roadmap process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4k2ht2d29o8ykyc6d44r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4k2ht2d29o8ykyc6d44r.jpg" alt="Alberto speaking about the Solidus roadmap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Solidus is a community made up of store owners, consultancies, individual developers, and more. Part of our strength is that so many businesses are build on the platform and are invested in its success.&lt;/p&gt;

&lt;p&gt;The biggest next step for the stakeholders team, the core team, and other members of the community is to finish to fleshing out the roadmap.&lt;/p&gt;

&lt;p&gt;We're also focused on increasing the funding of the &lt;a href="https://opencollective.com/solidus" rel="noopener noreferrer"&gt;Solidus Open Collective&lt;/a&gt; in order to hire a full time maintainer. Many of the tasks to support that are on the roadmap, but if you run a business on Solidus, consider a recurring contribution. If you look back on the everything that &lt;a href="https://www.johnhawthorn.com/" rel="noopener noreferrer"&gt;John Hawthorn&lt;/a&gt; did for the platform, it's clear that a small investment can pay massive dividends here.&lt;/p&gt;

&lt;p&gt;The community at large is focused on improving the extension ecosystem. Extensions enable new stores to jump onto the platform with minimal friction, so continuing to build on our robust suite of extensions is critical to the continued growth of the platform.&lt;/p&gt;

&lt;p&gt;Finally, expect to see more regional meetups and events around Solidus. The team and community are both aligned that these events do wonders for bringing us together and helping grow the platform.&lt;/p&gt;

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

&lt;p&gt;Solidus started when a bunch of developers wandered off into the mountains in British Columbia to try to improve the state of eCommerce on Ruby on Rails. While the locale has changed, we're still here, we're still making eCommerce better, and you better bet we're still hiking off into the woods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftsbj8tfdtanyl0r7nsej.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftsbj8tfdtanyl0r7nsej.jpg" alt="a group of Solidus Conf attendees hiking in the snow in Utah"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
