<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Orner</title>
    <description>The latest articles on DEV Community by Daniel Orner (@dorner).</description>
    <link>https://dev.to/dorner</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%2F275249%2Fe349116d-7659-40b9-bbcf-152d1f47b5c1.png</url>
      <title>DEV Community: Daniel Orner</title>
      <link>https://dev.to/dorner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dorner"/>
    <language>en</language>
    <item>
      <title>Building an Unsplash Slide Show on NeoCities</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Fri, 07 Jul 2023 18:53:04 +0000</pubDate>
      <link>https://dev.to/dorner/building-an-unsplash-slide-show-on-neocities-9mm</link>
      <guid>https://dev.to/dorner/building-an-unsplash-slide-show-on-neocities-9mm</guid>
      <description>&lt;p&gt;&lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt; is a great website that offers free images in a variety of formats and subjects. When we first went into lockdown in 2020, my home setup was two screens - my laptop, for actual work, and an external screen which is where my video calls took place, so I could still reference my work screen at the same time. &lt;/p&gt;

&lt;p&gt;However, while a call wasn't taking place, my top screen just kind of sat there, blank. I wanted something to look at during thinking breaks. Basically I wanted a screen saver, but you can't get those going on just one screen.&lt;/p&gt;

&lt;p&gt;Way back in the day, I had a number of personal pages on &lt;a href="https://en.wikipedia.org/wiki/GeoCities"&gt;GeoCities&lt;/a&gt;. This revolutionary site allowed you to write your own HTML and have it hosted for free! Turns out, there's a modern version of this, called &lt;a href="https://neocities.org/"&gt;NeoCities&lt;/a&gt;. It allows you to create simple sites with static files that are hosted for free.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://codepen.io/brianhaferkamp/post/full-screen-unsplash-slideshow"&gt;this post&lt;/a&gt; to make use of the &lt;code&gt;source.unsplash.com&lt;/code&gt; endpoint to show a random, full-page image on my browser which I made full-screen on the external monitor. (I use Firefox for my browsing, so I just stuck Chrome up there for the slide show). I also made use of &lt;a href="https://github.com/johnste/finicky"&gt;finicky&lt;/a&gt; so that all Google Meet links went to Chrome, while all other links stayed with Firefox.&lt;/p&gt;

&lt;p&gt;Problem! A couple of weeks ago, Unsplash removed the &lt;code&gt;source.unsplash.com&lt;/code&gt; endpoint. Instead, you need to use their API directly. Thankfully, the API is still free for low levels of usage, so I was able to modify the site to work with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Design
&lt;/h2&gt;

&lt;p&gt;Once you sign up, you'll get an API key. For these purposes, I didn't care too much about exposing it in a static site since I don't have any payment info on Unsplash and I'm not using it for anything else.&lt;/p&gt;

&lt;p&gt;The overall design is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want a "target" image div which will hold the image.&lt;/li&gt;
&lt;li&gt;We will fetch 30 images (the max we can) from the API in the collection we're interested in.&lt;/li&gt;
&lt;li&gt;We will loop through those images and show a new one once per minute.&lt;/li&gt;
&lt;li&gt;Once we've exhausted the ones we have, we'll fetch more. This means we should in general only be making 2 API requests per hour.&lt;/li&gt;
&lt;li&gt;If I don't like the image showing, I want an easy way to skip to the next one. I want to add a "refresh" button that will do that for me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm going to keep this super simple. The only dependency I have is jQuery which I'm using for animations. You could easily do this with CSS animations, but I'm not as used to those.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;First, here's the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;

&lt;span class="nc"&gt;.trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="m"&gt;200ms&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;background-repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/material-design-iconic-font/2.2.0/css/material-design-iconic-font.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"cursor: pointer;"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"refresh"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"zmdi zmdi-refresh-alt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"target"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// control the auto refresh rate -&amp;gt; 1000 = 1 second&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refreshRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// you can change the collections ID to switch these out. Or you can use any other query from the API if you want truly random images&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.unsplash.com/photos/random?w=1600&amp;amp;h=900&amp;amp;collections=879001&amp;amp;count=30&amp;amp;client_id=YOUR_API_KEY&amp;amp;orientation=landscape&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;// use this to cache our data&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// fetch the data, put it into the JSON blob, and pre-load the images from the data&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// display the next image&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;nextImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonData&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// we have more in the array&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;fadeOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;background-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`url(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;imageURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;fadeIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// we've exhausted the array, go get more&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nextImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intervalTimer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refreshRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#refresh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intervalTimer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nextImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intervalTimer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refreshRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Hide the images on load&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et voila! Hope someone out there finds this helpful!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>unsplash</category>
    </item>
    <item>
      <title>Ruby through the lens of Go</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Fri, 16 Apr 2021 18:33:23 +0000</pubDate>
      <link>https://dev.to/flipp-engineering/ruby-through-the-lens-of-go-3d68</link>
      <guid>https://dev.to/flipp-engineering/ruby-through-the-lens-of-go-3d68</guid>
      <description>&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1j61bs4s2rcz3pc0yc1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1j61bs4s2rcz3pc0yc1f.png" alt="Through The Lens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been an ardent Rubyist for over eight years now. I've dabbled in other languages, but at this point Ruby is the thing I'm most comfortable in.&lt;/p&gt;

&lt;p&gt;Over time, languages and programming styles change and so do the people who use them. While the power and elegance of Ruby still makes it my go-to for writing things, a recent foray into Go is making me rethink some of the things I've been taking for granted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Go?
&lt;/h2&gt;

&lt;p&gt;Go has been used at &lt;a href="https://corp.flipp.com" rel="noopener noreferrer"&gt;Flipp&lt;/a&gt; for some time now, although not widely in my team. I wanted to use Go to create a command-line executable, something that Ruby unfortunately isn't capable of doing. (There are options, such as &lt;a href="https://github.com/pmq20/ruby-packer" rel="noopener noreferrer"&gt;ruby-packer&lt;/a&gt;, but it seems like a "heavy" solution and doesn't seem to fit the Ruby paradigm.)&lt;/p&gt;

&lt;p&gt;Also, I was curious about Go as a whole, so I figured it was a great opportunity to learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language Philosophies
&lt;/h2&gt;

&lt;p&gt;Both Ruby and Go have extremely strong philosophies underpinning them. The guiding principle of Ruby is &lt;a href="https://en.wikipedia.org/wiki/Ruby_(programming_language)#Philosophy" rel="noopener noreferrer"&gt;programmer happiness&lt;/a&gt;. To achieve that aim, Ruby tries to be as flexible as possible, providing multiple ways of achieving the same thing in an effort to speak to each developer's preferred way of thinking. It supports lots of dynamism, allowing developers to "reopen" any class or module, or create whole new ways of interacting with code such as creating new &lt;a href="https://en.wikipedia.org/wiki/Domain-specific_language" rel="noopener noreferrer"&gt;DSLs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found it really interesting when reading up on Go that it &lt;em&gt;claims to have a similar philosophy&lt;/em&gt;. In &lt;a href="https://golang.org/doc/faq#ancestors" rel="noopener noreferrer"&gt;their FAQ&lt;/a&gt;, it says "[Go was] designed by thinking about what programmers do and how to make programming, at least the kind of programming we do, more effective, which means more fun."&lt;/p&gt;

&lt;p&gt;Having said that, it's pretty clear that Go is more of a &lt;a href="https://golang.org/doc/faq#creating_a_new_language" rel="noopener noreferrer"&gt;polemic&lt;/a&gt; against systems languages such as C. When they say "more fun", they mainly mean &lt;em&gt;more fun than C&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Go's real philosophy is a kind of extremist minimalism. The language is brutally strict about features that are and aren't allowed, keeping the feature set to an absolute minimum. The reasoning behind this is mainly that the language's raisons d'être are all about performance, speed, and simplicity. New features that might make the code itself slower or more complex are kiboshed.&lt;/p&gt;

&lt;p&gt;As an example of the philosophical difference, here's how you would find an element inside an array in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&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="c1"&gt;# 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's how you'd find it in Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ruby, filtering out even numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="mi"&gt;5&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="mi"&gt;7&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="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:even?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]{}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, language design is only part of the story behind a language. Just as important is the &lt;em&gt;ecosystem&lt;/em&gt;: the reusable code modules/packages, the people that manage them, which ones are "popular", what "best practices" are, etc.&lt;/p&gt;

&lt;p&gt;One of the things I love the most about Ruby is that it tends to coalesce around one or two &lt;em&gt;really popular&lt;/em&gt; libraries. &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails&lt;/a&gt; is the big one obviously, but over time you see libraries designed for a particular purpose "winning" over other things. This includes things like linting/code analysis (&lt;a href="https://github.com/rubocop/rubocop" rel="noopener noreferrer"&gt;Rubocop&lt;/a&gt;), authentication (&lt;a href="https://github.com/heartcombo/devise" rel="noopener noreferrer"&gt;Devise&lt;/a&gt;), testing (&lt;a href="https://rspec.info/" rel="noopener noreferrer"&gt;RSpec&lt;/a&gt; and &lt;a href="https://github.com/seattlerb/minitest" rel="noopener noreferrer"&gt;Minitest&lt;/a&gt;) and more. The emphasis is on making something good great rather than making a lot of different good things.&lt;/p&gt;

&lt;p&gt;In Go, it seems like packages themselves are nowhere near as popular as the language, compared to what I see elsewhere. I haven't been in the ecosystem long, but it seems to be rare that articles about Go coalesce on a particular solution similar to Rails or something like &lt;a href="https://lodash.com/" rel="noopener noreferrer"&gt;lodash&lt;/a&gt; or &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;express&lt;/a&gt; in the JavaScript world. Instead, the main feeling when asked "How do I do X" in Go seems to be "do it yourself". The kind of thing that in Ruby would be derided as "plumbing" is "real code" in Go; the goal is to make you think about every single line you write.&lt;/p&gt;

&lt;p&gt;Go's lack of generics make it difficult to make an all-purpose utility library to make these sorts of things easier, so maybe once they land in a year or two the rawness of the language could be mitigated. But for now, Go and Ruby feel like as far apart as they could be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can we learn?
&lt;/h2&gt;

&lt;p&gt;The strictness of Go means that there likely aren't too many things that we could import into it from Ruby and similar languages. And nor should we; one of the refrains of the Go philosophy is that languages should be &lt;em&gt;different&lt;/em&gt; rather than similar, and Go is outspoken in its adherence to its own unique identity. &lt;/p&gt;

&lt;p&gt;However, I think that some of the surprising advantages I found in Go can be moved over to Ruby without much work other than a slight shift in mindset. (There are other amazing things Go provides that Ruby simply can't do, and in fact might start affecting the beautiful things about it - this post is not about all the nice things in Go but the things that we can do with our Ruby code to gain some of the same advantages.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep code and data separate
&lt;/h3&gt;

&lt;p&gt;Here is how you define a method on a type in Go. (This example intentionally has a code smell, where I'm sending an e-mail from something that shouldn't be able to or care about it.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customerEmail&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;sendProductEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customerEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ...&lt;/span&gt;

&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"some-product-id"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"me@example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The usage of the method looks the way we expect it, but wait... something's missing from the function body. &lt;strong&gt;Where's the &lt;code&gt;this&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is that there isn't one. Not because the idea doesn't exist (it does - it's called a &lt;em&gt;receiver&lt;/em&gt; and Ruby has the same idea) but because the syntax calls out a very important fact, which is that &lt;em&gt;methods and data do not need to live together in an object&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To make this abundantly clear, let's realize that we made a stupid mistake and move the &lt;code&gt;sendEmail&lt;/code&gt; function from the product to a top-level function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customerEmail&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;sendProductEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customerEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// ...&lt;/span&gt;

&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"some-product-id"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"me@example.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's changed in our function body?&lt;/p&gt;

&lt;p&gt;The answer: Absolutely nothing. The definition and usage changed, but the function body is identical. This is a very strong hint that &lt;em&gt;methods work on data&lt;/em&gt;. And whether the methods and data "live together" in an object or not, the exact same thing is happening.&lt;/p&gt;

&lt;p&gt;Keeping methods out of objects helps to reduce the urge to introduce side effects. And that means you can often model your functions as pure input/output, which has &lt;a href="https://www.deadcoderising.com/2017-06-13-why-pure-functions-4-benefits-to-embrace-2/" rel="noopener noreferrer"&gt;a whole whack of advantages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As an example, take a Ruby implementation of the above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;send_product_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_email&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;Let's do the same refactor now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;send_product_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've had to change our function body to accommodate this now.&lt;/p&gt;

&lt;p&gt;So how can we achieve the same flexibility in Ruby? Simple. &lt;em&gt;Treat data as data and methods as methods.&lt;/em&gt; Prefer static methods that work on data. This has the added advantage of enabling you to "pipe" data more easily without having to fall back to solutions I've always found odd like &lt;code&gt;tap&lt;/code&gt; and &lt;code&gt;yield_self&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;send_product_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'me@example.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The functions stay in the class definition (Go doesn't have class functions, which IMO makes it a bit harder to organize) but are static and don't use or change any object state. &lt;/p&gt;

&lt;p&gt;For example, let's say we've decided to move this function to a new module which deals with all the e-mail sending. The function doesn't change at all any more - in fact, even the pattern of calling it stays the same. The only thing that changes is the class name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;send_product_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;EmailService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'me@example.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously this won't work 100% of the time, but I've found this pattern to be much easier to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use types and not hashes
&lt;/h2&gt;

&lt;p&gt;Go is a statically typed language, in its own maverick way. It doesn't support inheritance, and mainly works through structs and interfaces (which is a great feature that Ruby can approximate, much less elegantly, via "duck-typing" and &lt;code&gt;respond_to?&lt;/code&gt; calls). &lt;/p&gt;

&lt;p&gt;Ruby is dynamically typed, and in a typical Ruby project you'll see classes, OpenStructs (a class that basically makes a hash "pretend" to be an object by using dot-notation), and other solutions, but in many cases you're using hashes to store your data.&lt;/p&gt;

&lt;p&gt;There are some real benefits to static typing, not the least that it makes IDE's much smarter at telling you when you're doing something wrong. Even better, types implicitly document what your data looks like much better than hashes.&lt;/p&gt;

&lt;p&gt;Ruby is slowly starting to drift towards better type functionality, with first &lt;a href="https://sorbet.org/" rel="noopener noreferrer"&gt;Sorbet&lt;/a&gt; and then &lt;a href="https://github.com/ruby/rbs" rel="noopener noreferrer"&gt;RBS&lt;/a&gt;. But you don't need any static typing tool in your own code to do things smarter.&lt;/p&gt;

&lt;p&gt;It's not like Ruby doesn't already support structs! Check the difference here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# @param json [Hash]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_dashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="no"&gt;DashboardCreator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;json&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;graphs: &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:graphs&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:owner&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my opinion, it doesn't matter if &lt;code&gt;DashboardCreator.call&lt;/code&gt; is defined with keyword arguments or an options hash. The fact that the input &lt;code&gt;json&lt;/code&gt; is a hash means that your code has no idea if, for example, &lt;code&gt;:name&lt;/code&gt; is a valid key into your JSON.&lt;/p&gt;

&lt;p&gt;Hashes are maps. Maps are &lt;em&gt;key-value pairs&lt;/em&gt;. They're meant for lookups. For some reason, Ruby has latched onto this idea that they should be used as options, keywords, or attributes. This is confusing and error-prone.&lt;/p&gt;

&lt;p&gt;Now here's the same thing with structs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# @attr graphs [Array&amp;lt;Graph&amp;gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# @attr name [String]&lt;/span&gt;
&lt;span class="c1"&gt;# @attr owner [String]&lt;/span&gt;
&lt;span class="no"&gt;Dashboard&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;:graphs&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;:owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# @param dashboard [Dashboard]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_dashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;DashboardCreator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how I've packaged up all the data into a single object which is "strongly typed", or as strong as Ruby can get for now. We (and our IDEs) now know absolutely what that object looks like.&lt;/p&gt;

&lt;p&gt;Now there might be cases where all we've done here is push up the error cases where our parsed JSON doesn't match what we expect. For example, we might need a method that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# @param json [Hash]&lt;/span&gt;
&lt;span class="c1"&gt;# @return [Dashboard]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_dashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"No graphs!"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:graphs&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
   &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:graphs&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="no"&gt;Dashboard&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="n"&gt;json&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;graphs: &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:graphs&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;owner: &lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:owner&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;keyword_init: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thing is, we've moved the error into a method &lt;em&gt;whose only job is to parse JSON&lt;/em&gt;. We can do further validations here. But importantly, we don't encode our expectations about JSON into all the downstream functions. Only one thing cares about JSON itself. Everything else cares about dashboards.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplify, simplify
&lt;/h3&gt;

&lt;p&gt;Ruby's dynamism allows for some truly concise and beautiful-looking code. But like always, you need to make sure your toolbox is using the right tools for the right situations.&lt;/p&gt;

&lt;p&gt;When you're building a &lt;em&gt;framework&lt;/em&gt;, using the more advanced metaprogramming features like &lt;code&gt;define_method&lt;/code&gt; and &lt;code&gt;method_missing&lt;/code&gt; can work well and provide a useful interface. But using it in your code can cause a world of pain.&lt;/p&gt;

&lt;p&gt;Just because you &lt;em&gt;can&lt;/em&gt; do something in Ruby doesn't mean you &lt;em&gt;must&lt;/em&gt; do it. I've moved over to preferring &lt;em&gt;code generation&lt;/em&gt; over &lt;em&gt;code reflection&lt;/em&gt;. In other words, using generators to create the code you care about rather than deciding what your code should do at runtime.&lt;/p&gt;

&lt;p&gt;For example, let's say you have a web parser that has to fetch a number of item fields by using different XPath or CSS selectors. We want to define a parent class that defines all these fields and child classes that separately specify how to fetch them for different websites. Here are a couple of different ways to achieve this.&lt;/p&gt;

&lt;p&gt;First, how we want to use this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Item&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;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Site1Parser&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Parser&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/some/path/to/name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Site1Parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="c1"&gt;# the value parsed from the site&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Way number 1 uses &lt;code&gt;method_missing&lt;/code&gt; to set attributes to an item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parser&lt;/span&gt;

  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:item&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="no"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;super&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;Option 2 goes a bit more explicit by actually defining methods using &lt;code&gt;define_method&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parser&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:item&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="no"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;define_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Not implemented yet!"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leaving alone the possible debugger confusion, when we humans are looking at either of these options, we need a lot of information in addition to the body of the method. What are valid values for &lt;code&gt;method&lt;/code&gt; or &lt;code&gt;field&lt;/code&gt;? What possible arguments could be passed? What does &lt;code&gt;self&lt;/code&gt; mean at this point?&lt;/p&gt;

&lt;p&gt;What if instead, we created a &lt;a href="https://guides.rubyonrails.org/generators.html" rel="noopener noreferrer"&gt;generator&lt;/a&gt; which created code for you? Something that might look like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parser&lt;/span&gt;

  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:item&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Not implemented yet!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Not implemented yet!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"Not implemented yet!"&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;In this case, rather than writing all this code yourself, you rely on the generator to do it for you. Some benefits of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code is much easier to understand, because you can actually look at each method and see exactly what it calls, without having to step through it or check to see what &lt;code&gt;Item.members&lt;/code&gt; returns.&lt;/li&gt;
&lt;li&gt;Your codebase becomes searchable, both by you and by code analysis tools. Your function calls are explicit and you can logically step through them without confusion.&lt;/li&gt;
&lt;li&gt;You can more easily override specific methods as necessary when they're already there for you to do so. This is the kind of thing object oriented design is really good at. And you can easily tell what methods you &lt;em&gt;have&lt;/em&gt; to override.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously you run into other problems on the framework side (when you add a field to &lt;code&gt;Item&lt;/code&gt;, how do you make sure you can update the code without blowing away anything you've changed in it?) which might involve more advanced &lt;a href="https://medium.com/flippengineering/using-rubocop-ast-to-transform-ruby-files-using-abstract-syntax-trees-3e352e9ac916" rel="noopener noreferrer"&gt;abstract syntax tree&lt;/a&gt; munging. But I'll leave this here as a thought experiment, nevertheless.&lt;/p&gt;

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

&lt;p&gt;Both Ruby and Go are amazing languages in their own way, and both provide numerous benefits in different directions. There are definitely things I wish I could change about Go, but the nice thing about Ruby is that I know I &lt;em&gt;can&lt;/em&gt; change some things about it all on my own!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>go</category>
    </item>
    <item>
      <title>Your Programming Toolbox: Functional and Object-Oriented Paradigms</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Fri, 27 Nov 2020 14:31:55 +0000</pubDate>
      <link>https://dev.to/flipp-engineering/your-programming-toolbox-functional-and-object-oriented-paradigms-3f6p</link>
      <guid>https://dev.to/flipp-engineering/your-programming-toolbox-functional-and-object-oriented-paradigms-3f6p</guid>
      <description>&lt;p&gt;Object-oriented (OO) and functional programming (FP) are the two most popular programming paradigms in use today. Most languages provide at least some features from one or both of them. Although I don't intend to dive into what exactly OO and FP are, as there are &lt;a href="https://www.freecodecamp.org/news/object-oriented-programming-concepts-21bb035f7260/"&gt;many&lt;/a&gt;, &lt;a href="https://www.sitepoint.com/what-is-functional-programming/"&gt;many&lt;/a&gt; articles already written that attempt to do this, I do want to try to weigh in on a corollary argument: "Which one's better?"&lt;/p&gt;

&lt;p&gt;There have been countless keystrokes shed in pursuit of this age-old question. For example, we've got &lt;a href="https://medium.com/better-programming/object-oriented-programming-the-trillion-dollar-disaster-92a4b666c7c7"&gt;Ilya Suzdalnitzky&lt;/a&gt; proclaiming the end of OO as we know it, and a number of other articles passionately &lt;a href="https://medium.com/young-coder/the-case-against-oop-is-wildly-overstated-572eae5ab495"&gt;arguing the opposite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This combat area has been re-tread so often, it's practically a no-go zone. The most thoughtful articles on this are can be distilled into the following insights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Any paradigm can be done well, or badly.&lt;/li&gt;
&lt;li&gt;Both FP and OO have advantages in certain cases and disadvantages in others.&lt;/li&gt;
&lt;li&gt;Both FP and OO have wide-ranging definitions, and depending on what you're doing and in what language, you may &lt;em&gt;think&lt;/em&gt; you're doing one or the other but someone else may disagree, or maybe you're doing a mishmash of them both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As time goes on, it becomes clear that many popular languages are diving deeper into becoming &lt;strong&gt;multi-paradigm&lt;/strong&gt; - they allow you to write in procedural, OO or FP and you can pick and choose your language features.&lt;/p&gt;

&lt;p&gt;Because of this, you can take bits and pieces of these different approaches and try to synthesize them into the best of both worlds. I'd like to touch on my thoughts on &lt;em&gt;what&lt;/em&gt; the good pieces of the two approaches are, and &lt;em&gt;when&lt;/em&gt; to use the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Human Touch
&lt;/h2&gt;

&lt;p&gt;One of the major differences between OO and FP is in the visceral, intellectual way that people see code. In OO "everything is an object"; in FP "everything is a function". Of course, neither of those statements is entirely true. But at a high level, people who are more mathematically inclined seem to enjoy thinking of code as functions, while those less so find it more intuitive to think of code as objects.&lt;/p&gt;

&lt;p&gt;I personally fall into the latter camp - my first task in any programming problem is to think of the "things" and after that, what has to happen with those "things". Because of this, my starting approach is OO, and then I start thinking of how I can use aspects of FP to improve some of the shortcomings of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How functional?
&lt;/h2&gt;

&lt;p&gt;In my opinion, the power of FP comes from thinking about &lt;a href="https://sidburn.github.io/blog/2016/03/14/immutability-and-pure-functions"&gt;data as &lt;em&gt;immutable&lt;/em&gt; and functions as &lt;em&gt;pure&lt;/em&gt; (or pure-ish)&lt;/a&gt;. The main benefits are testability and confidence. You can write your tests knowing that given a particular input, you will &lt;em&gt;always&lt;/em&gt; get the desired output, since you don't need to worry about the state of the object you're calling it on.&lt;/p&gt;

&lt;p&gt;Having said that, I do &lt;em&gt;not&lt;/em&gt; advocate a complete FP approach unless your whole team is onboard with that, because then you have to wade into the waters of monads and functors and higher-level functions and functional math (ew, math). I think &lt;a href="https://medium.com/@dmitri145/nothing-is-actually-further-away-from-the-truth-a55bf5aab93f"&gt;this article&lt;/a&gt;, written about JavaScript, gives a really good overview of how to take the good parts of FP without being slavish.&lt;/p&gt;

&lt;p&gt;My take on pure functions is more along the lines of: "Your function should &lt;em&gt;either&lt;/em&gt; be pure, &lt;em&gt;or&lt;/em&gt; accomplish something in the real world, but not both."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Nitty Gritty
&lt;/h2&gt;

&lt;p&gt;I'll be using Ruby in my examples, as it's the multi-paradigm language I'm most comfortable with, but it's almost always possible to convert these examples to JavaScript, Java, Python, etc.&lt;/p&gt;

&lt;p&gt;One of my earlier revelations about FP and OO is that from a technical perspective, they are practically interchangable. If you think of your object as &lt;em&gt;data&lt;/em&gt;, and the functions/methods as acting on that data, then the following two chunks of code are equivalent - except that the first is in OO-land and the second is in FP-land.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Person&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;:status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;marry!&lt;/span&gt;
    &lt;span class="vi"&gt;@status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"married"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&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;"Daniel Orner"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;marry!&lt;/span&gt;
&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;# married&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Person&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;:status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;marry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Person&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="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"married"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;marry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Person&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;"Daniel Orner"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt; &lt;span class="c1"&gt;# married&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They accomplish the same thing - except that in OO-land, the function lives &lt;em&gt;with the data&lt;/em&gt; and &lt;em&gt;changes the data&lt;/em&gt;, while in FP-land, the function lives &lt;em&gt;outside the data&lt;/em&gt; and &lt;em&gt;returns a new set of data&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Most important things that can be achieved by one paradigm can be achieved by the other as well, as I'll explore below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Organization
&lt;/h3&gt;

&lt;p&gt;One use of classes is a way to &lt;em&gt;organize&lt;/em&gt; your methods. If all your methods dealing with a person are in the Person class, it's easy to parse out where to find the thing you're looking for.&lt;/p&gt;

&lt;p&gt;FP has a corresponding way to do this as well, although it's more language-specific. In JavaScript, you might put all your functions in a single file. In other languages, you might use packages, namespaces, modules, etc. and ensure that you use "static" or "module-level" functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;## OO-land&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;marry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;## FP-land&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# person_operations.rb&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;PersonOperations&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;marry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leaves the Person class as a &lt;em&gt;value object&lt;/em&gt; - it's more of a type than a class - and all operations live outside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encapsulation
&lt;/h3&gt;

&lt;p&gt;With OO, you can have private state which isn't exposed, and you only expose essentially an API for external code to call. How could you do that with FP?&lt;/p&gt;

&lt;p&gt;The short answer is, not easily. You can use closures to achieve some level of encapsulation, but it will largely be on a global level rather than encapsulating data in an individual object. This is one of the weaknesses of FP.&lt;/p&gt;

&lt;p&gt;However, the bigger question is &lt;em&gt;why&lt;/em&gt; you are encapsulating data in the first place. If you're using FP, your functions are &lt;em&gt;pure&lt;/em&gt; and your data is &lt;em&gt;immutable&lt;/em&gt; - so you can't have anything accidentally or maliciously change your data.&lt;/p&gt;

&lt;p&gt;The main reason I see to encapsulate data is as a form of documentation: This class has ten fields, but only five of them are relevant to anything outside the methods in &lt;em&gt;this&lt;/em&gt; module that do &lt;em&gt;these&lt;/em&gt; things to it. (Honestly, once you've even had this thought, it usually turns me to "do these five fields &lt;em&gt;really&lt;/em&gt; belong with this data, or should they be somewhere else?")&lt;/p&gt;

&lt;h3&gt;
  
  
  Composition
&lt;/h3&gt;

&lt;p&gt;One of the basic tenets of functional programming is using composition to combine functions together. For example, here's a way to take two functions and combine them into one that does both things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;add_one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&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;double_and_add_one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_one&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;
&lt;span class="n"&gt;double_and_add_one&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In OO-land, composition means modeling things as a "has-a" relationship instead of an "is-a" relationship. For example, instead of treating an Employee as a subclass of Person, you might instead give your employee an attribute of type Person. It's not as intuitive, but it works just as well while avoiding some of the &lt;a href="https://en.wikipedia.org/wiki/Composition_over_inheritance#Benefits"&gt;pitfalls of inheritance&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;

  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;
    &lt;span class="vi"&gt;@last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;full_name&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Forwardable&lt;/span&gt;
  &lt;span class="nb"&gt;attr_accessor&lt;/span&gt; &lt;span class="ss"&gt;:person&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Person&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="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;def_delegators&lt;/span&gt; &lt;span class="ss"&gt;:@person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:full_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to use what?
&lt;/h2&gt;

&lt;p&gt;In general, you can use what I feel are the "important parts" of FP easily within an OO framework, but not so much the other way around. For example, I like the idea of value objects. My sweet spot is leaving all functions related to &lt;em&gt;reading or writing attributes&lt;/em&gt; in the class, but anything bigger than that outside of it.&lt;/p&gt;

&lt;p&gt;But OO has its advantages as well. When should you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have methods live with your data&lt;/li&gt;
&lt;li&gt;Provide an inheritance structure&lt;/li&gt;
&lt;li&gt;Allow overriding of "super" methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've found two really good cases for these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Framework code&lt;/li&gt;
&lt;li&gt;Value object hierarchy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all other cases, I'd put a big question mark on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Framework code
&lt;/h3&gt;

&lt;p&gt;When I use the term "framework", I mean you are defining a way to accomplish a particular task without actually &lt;em&gt;doing&lt;/em&gt; that task - generally when you have to have many slightly different ways of doing the same thing. Some examples from my past include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A web scraper that saves data to a particular schema/model&lt;/li&gt;
&lt;li&gt;A producer that takes data and maps it to Kafka topic&lt;/li&gt;
&lt;li&gt;A CSV parser that needs to be able to handle different formats for different clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all these cases, you want to have some "base behavior" defined which you can easily use, but you want to be able to override bits and pieces, and provide helper methods.&lt;/p&gt;

&lt;p&gt;Using a pure FP approach in this case is definitely possible, but a lot of work, because the "overriding" piece needs to happen via the use of composition and hooks.&lt;/p&gt;

&lt;p&gt;Here's a sample parser (without showing method implementation) that takes in a CSV file and outputs a structured array to save to the database, in OO-land:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parser&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;structured_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;results&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this framework, you can write a parser only by overriding the two methods that every parser needs to write, &lt;code&gt;parse_row&lt;/code&gt; and &lt;code&gt;parse_headers&lt;/code&gt;. The other functionality "just works" since it uses the parent code.&lt;/p&gt;

&lt;p&gt;In FP-world, you'd need to implement it similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Parser&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# &lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;structured_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'd need to pass in the &lt;code&gt;parse_header&lt;/code&gt; and &lt;code&gt;parse_row&lt;/code&gt; functions to the &lt;code&gt;parse&lt;/code&gt; method to be able to override this behavior.&lt;/p&gt;

&lt;p&gt;This example doesn't look too bad, but what if there are additional steps you want to add functionality to? Inheritance, and particularly the &lt;code&gt;super&lt;/code&gt; keyword, allows you to easily "wrap" a function around specialized code without having to do anything special on the parent class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyParser&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Parser&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;new_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handle_special_characters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In FP-land, you'd need to do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Parser&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse_header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;clean_up_data&lt;/span&gt;
      &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;internal_clean_up_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you want to "wrap" the original behavior, you'd need to grab a reference to the function on the module that does it and call it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;my_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handle_special_characters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:clean_up_data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"my_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_parse_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_parse_header_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clean_up_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As your framework grows in functionality and usefulness, you'll find more and more of these overrides are necessary as the use cases expand. If you don't want to build them all into your own code, you &lt;em&gt;need&lt;/em&gt; to allow people to make changes as necessary, and OO allows a much cleaner way to do that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inheritance in Value Objects
&lt;/h3&gt;

&lt;p&gt;I think there is also space for OO-style inheritance when dealing with your data, as long as you can absolutely model that data as an "is-a" relationship. The main difference is that your classes are things that &lt;em&gt;are&lt;/em&gt;, not things that &lt;em&gt;do&lt;/em&gt;. You can do this with a &lt;code&gt;Service&lt;/code&gt;, &lt;code&gt;Employee&lt;/code&gt;, &lt;code&gt;House&lt;/code&gt;, etc. - not with a &lt;code&gt;Parser&lt;/code&gt;, &lt;code&gt;Manager&lt;/code&gt;, or &lt;code&gt;DatabaseLayer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if a user has a name, e-mail address and login, and so does an employee, it makes sense to have Employee inherit from User since you get its fields for free, and you can safely say that a) every Employee &lt;em&gt;is a&lt;/em&gt; user, and b) Employee would never change the behavior defined in User in such a way to violate the &lt;a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle"&gt;Liskov Substitution Principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also freely use interfaces or abstract classes in languages that support it, and approximate them in languages that don't (e.g. defining a class in Ruby where every method consists of a body that raises a &lt;code&gt;NotImplementedError&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;However, I'd consider it a code smell going past that in non-framework code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;When I was first introduced to FP a few years ago, I had a hard time wrapping my head around it because so much of the discussion around it felt so academic. As I've come to understand the real-world benefits of using it, I'm more able to reap those benefits without entirely overhauling how I work. I'm still feeling my way towards the golden road of combining the features of these paradigms in the right ways in the right cases.&lt;/p&gt;

&lt;p&gt;Hope this is helpful to others out there as well!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>functional</category>
      <category>objectoriented</category>
    </item>
    <item>
      <title>Code Reuse in RSpec</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Fri, 20 Nov 2020 14:41:10 +0000</pubDate>
      <link>https://dev.to/flipp-engineering/code-reuse-in-rspec-18n4</link>
      <guid>https://dev.to/flipp-engineering/code-reuse-in-rspec-18n4</guid>
      <description>&lt;p&gt;When writing your tests with RSpec, you have a plethora of ways to help DRY up your code and keep it clean and reusable. So many ways, in fact, that it can be pretty confusing to keep track of when you should do what. Forthwith, a quick reference for you!&lt;/p&gt;

&lt;h1&gt;
  
  
  Contexts
&lt;/h1&gt;

&lt;p&gt;First, an explanation of what I mean by "context". In RSpec-world, a context basically refers to setup code and methods that are available to the current example. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;before&lt;/code&gt;, &lt;code&gt;after&lt;/code&gt;, and &lt;code&gt;around&lt;/code&gt; hooks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;let!&lt;/code&gt; declarations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subject&lt;/code&gt; declarations&lt;/li&gt;
&lt;li&gt;helper methods&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Nested Groups
&lt;/h1&gt;

&lt;p&gt;The simplest way of reusing code is when your tests all belong to the same file. You can reuse your &lt;em&gt;context&lt;/em&gt; simply by nesting your tests within the same group. Remember that you declare an example group with the &lt;code&gt;describe&lt;/code&gt; or &lt;code&gt;context&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Here's an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Taxi&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:driver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Driver&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="s1"&gt;'John'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:taxi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Taxi&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="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should take a fare'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_fare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;income&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"should add a drive to the driver's history"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_drive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should get an address to drive to'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"123 Main St."&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_drive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St."&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;You'll notice that all three examples in the group can make use of the &lt;code&gt;taxi&lt;/code&gt; and &lt;code&gt;driver&lt;/code&gt; declarations, since it's declared at the group level.&lt;/p&gt;

&lt;p&gt;But note that one of those examples doesn't actually need a reference to the driver, and doesn't need the setup code that assigns it! This is a contrived example, but you can easily spend hundreds of lines setting up data in your tests, and you want that data to be targeted so you're not re-creating data where you don't need it. This is where you can nest your tests a little deeper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Taxi&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:taxi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Taxi&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="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should get an address to drive to'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"123 Main St."&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_drive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'with driver'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:driver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Driver&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;name: &lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should take a fare'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_fare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;income&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, only the tests that need a driver have access to one, but all tests still have access to the taxi.&lt;/p&gt;

&lt;h1&gt;
  
  
  Shared Context
&lt;/h1&gt;

&lt;p&gt;This is all well and good when testing a single class or feature. But what about when you're testing a &lt;em&gt;category&lt;/em&gt; of related features? This is when shared contexts can be useful.&lt;/p&gt;

&lt;p&gt;Shared contexts are a way to define a reusable context that can be used across files, including declarations, hooks and even helper methods. Let's say that we not only care about taxis, but about all kinds of vehicles.&lt;/p&gt;

&lt;p&gt;First you define your shared context in a file that's included by all tests. If you don't have many of these contexts, you can put them inside your main &lt;code&gt;spec_helper&lt;/code&gt; file. If you have more, you can separate them out into its own file and manually include it from your test files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file: /spec/vehicles/shared_setup.rb&lt;/span&gt;
&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shared_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"with driver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drive_vehicle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;
    &lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drive!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:driver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Driver&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;"John"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wake_up!&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file: /spec/vehicles/car_spec.rb&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s2"&gt;"./shared_setup.rb"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;include_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"with driver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Car&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="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"should drive"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# this is true because wake_up! was called in the before hook&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_awake&lt;/span&gt; 
    &lt;span class="n"&gt;drive_vehicle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_driving&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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;Here, you've got access to the driver and your helper methods from your shared context in this file, and you can include that context in any code that needs a driver.&lt;/p&gt;

&lt;h1&gt;
  
  
  Shared Examples
&lt;/h1&gt;

&lt;p&gt;Shared &lt;em&gt;contexts&lt;/em&gt; help to create data or behavior for your tests. Shared &lt;em&gt;examples&lt;/em&gt; actually &lt;em&gt;are&lt;/em&gt; tests which you can run in multiple places.&lt;/p&gt;

&lt;p&gt;Why would you do this? A classic reason is inheritance (or composition, mixins, or any other way you have to reuse your own code). If you have a number of classes or modules that all behave a certain way, you can write a single set of examples that should apply to all of them and ensure that they all pass.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/vehicles/shared_setup.rb&lt;/span&gt;
&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shared_examples&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a vehicle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should be able to move'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"123 Main St."&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/vehicles/car_spec.rb&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s2"&gt;"./shared_setup.rb"&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;it_should_behave_like&lt;/span&gt; &lt;span class="s2"&gt;"a vehicle"&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# more tests only for cars&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 running the suite, all the shared examples will run along with the specific examples. Shared examples can make use of the current context, so for example you can use &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;subject&lt;/code&gt; declarations to act as parameters, or you can &lt;a href="https://relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples#passing-parameters-to-a-shared-example-group" rel="noopener noreferrer"&gt;pass parameters directly&lt;/a&gt; to shared examples.&lt;/p&gt;

&lt;h1&gt;
  
  
  When?
&lt;/h1&gt;

&lt;p&gt;Here's a rubric as to when to use these three features:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmax7ida0ys93urkvfhxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmax7ida0ys93urkvfhxk.png" alt="RSpec Rubric"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Factories
&lt;/h1&gt;

&lt;p&gt;This is more specific to ActiveRecord, but factories are a way to DRY up your data creation! Factories have an advantage over fixtures in that you aren't limited to specific, hand-crafted records but can define &lt;em&gt;how&lt;/em&gt; to create data.&lt;/p&gt;

&lt;p&gt;The most common way to use factories is to include the &lt;a href="https://github.com/thoughtbot/factory_bot" rel="noopener noreferrer"&gt;FactoryBot&lt;/a&gt; gem. Once set up, you can use it to manage your data creation.&lt;/p&gt;

&lt;p&gt;As an example, let's say you can create taxis that are yellow, or that are New York taxis, or that are out of service. FactoryBot allows you to create &lt;em&gt;traits&lt;/em&gt; to reuse this behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/factories/taxis.rb&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;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:taxi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;trait&lt;/span&gt; &lt;span class="ss"&gt;:yellow&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"yellow"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;trait&lt;/span&gt; &lt;span class="ss"&gt;:new_york&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'New York'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;trait&lt;/span&gt; &lt;span class="ss"&gt;:out_of_service&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'OUT_OF_SERVICE'&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# /spec/taxi_spec.rb&lt;/span&gt;
&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Taxi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should have the right attributes'&lt;/span&gt;
    &lt;span class="n"&gt;taxi&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;:taxi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:new_york&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="ss"&gt;:yellow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:out_of_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'yellow'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'New York'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taxi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'OUT_OF_SERVICE'&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 should you create a factory vs. just creating your data manually? Some good rules of thumb are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you have tests that don't need to understand the underlying data model but need to create the data&lt;/li&gt;
&lt;li&gt;When you've identified a pattern of data creation that's called from multiple places that you can simplify&lt;/li&gt;
&lt;li&gt;When you want to encapsulate how your data looks in a simpler, centralized way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RSpec is powerful but can be confusing, so hopefully this explains the different features in an organized way. Happy coding!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>testing</category>
    </item>
    <item>
      <title>Linting only changed files with GitHub Actions</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Mon, 24 Feb 2020 16:29:23 +0000</pubDate>
      <link>https://dev.to/flipp-engineering/linting-only-changed-files-with-github-actions-4ddp</link>
      <guid>https://dev.to/flipp-engineering/linting-only-changed-files-with-github-actions-4ddp</guid>
      <description>&lt;p&gt;In our team at Flipp, like many companies, we have a venerable and enormous Rails monolith which is feeling its age. We've started the long process of breaking it up, but in the meantime there are hundreds of thousands of lines of code which are changed piecemeal across dozens of features.&lt;/p&gt;

&lt;p&gt;We introduced &lt;a href="https://github.com/rubocop-hq/rubocop" rel="noopener noreferrer"&gt;Rubocop&lt;/a&gt;, the Ruby linter, a few years ago, and very slowly started updating the code as we changed it. We used a &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" rel="noopener noreferrer"&gt;pre-commit Git hook&lt;/a&gt; to ensure that our developers couldn't even commit code that didn't pass the linter. We used a modified version of &lt;a href="https://github.com/melch/dirtycop" rel="noopener noreferrer"&gt;dirty_cop&lt;/a&gt; - at the time this was just a gist we found online - which only runs the linter on modified files.&lt;/p&gt;

&lt;p&gt;As time went on, this solution started to become less and less ideal. Our codebase was so big and so old that just starting the linter could take upwards of 20 seconds. We took on new team members, not all of whom installed the pre-commit hook; other teams began submitting pull requests as well. This just wasn't a great solution to ensure we had a clean codebase with a consistent style.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Project
&lt;/h1&gt;

&lt;p&gt;Our first instinct was to move the lint check to our CI tool, &lt;a href="https://circleci.com/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;. It's really powerful, but there was one big problem - CircleCI runs on &lt;em&gt;pushes&lt;/em&gt;, not &lt;em&gt;pull requests&lt;/em&gt;. It would have no way of knowing what files changed in the PR.&lt;/p&gt;

&lt;p&gt;Finally, we started investigating &lt;a href="https://help.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;. This seemed like it ticked all the boxes - it's a CI tool that can be set to run on pull requests, meaning it would be possible to detect which files were changed in that PR. Even better, because it runs within the GitHub ecosystem, it would be relatively simple to add comments or even commits to the PR itself fixing or commenting on the issues that were found.&lt;/p&gt;

&lt;p&gt;Luckily enough, we found two existing actions that looked right up our alley:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/trilom/file-changes-action" rel="noopener noreferrer"&gt;file-changes-action&lt;/a&gt; outputs the files changed during a PR.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/samuelmeuli/lint-action" rel="noopener noreferrer"&gt;lint-action&lt;/a&gt; runs a linter (it supports many languages, not just Ruby) and does auto-fixing and commenting as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On with the show!&lt;/p&gt;

&lt;h1&gt;
  
  
  The Solution
&lt;/h1&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-ruby@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2.4'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache gems&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-gems-${{ env.cache-name }}-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
        &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ runner.os }}-gems-&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bundle install&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;bundle config our_gem_repo.com our_username:${{ secrets.GEM_REPO_PASSWORD }}&lt;/span&gt;
        &lt;span class="s"&gt;bundle config set without 'production staging'&lt;/span&gt;
        &lt;span class="s"&gt;bundle config path vendor/bundle&lt;/span&gt;
        &lt;span class="s"&gt;bundle install&lt;/span&gt;
        &lt;span class="s"&gt;git checkout Gemfile.lock&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get file changes&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_file_changes&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dorner/file-changes-action@v1.2.0&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;githubToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;plaintext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Echo file changes&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;echo Changed files: ${{ steps.get_file_changes.outputs.files }}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run lint&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dorner/lint-action@v1.3.3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;rubocop_bundler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;auto_fix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;rubocop_bundler_args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-R --fail-level C ${{ steps.get_file_changes.outputs.files}}&lt;/span&gt;



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

&lt;/div&gt;
&lt;h1&gt;
  
  
  The Issues
&lt;/h1&gt;

&lt;p&gt;When developing this, we ran across a number of issues we were able to solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our gems are hosted on a private repo. To enable access to them, we had to &lt;a href="https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets" rel="noopener noreferrer"&gt;store a secret&lt;/a&gt; in the repo with our credentials.&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;Gemfile.lock&lt;/code&gt; uses an old version of Bundler (1.17). Newer versions of Bundler automatically update the &lt;code&gt;Gemfile.lock&lt;/code&gt; to indicate that it was bundled with that version - which means older Bundlers will subsequently fail. This is fine if you're not doing anything with that file, but the &lt;code&gt;auto_fix&lt;/code&gt; option on the linter means it may commit the contents of the workspace back to the repo, along with the modified &lt;code&gt;Gemfile.lock&lt;/code&gt;. To solve this, we had to use &lt;code&gt;git checkout&lt;/code&gt; to keep the file pristine before continuing.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;file-changes-action&lt;/code&gt; outputs the files as a JSON array of strings. This isn't so convenient to pass into a shell command, so I made a fork of it and added the &lt;code&gt;plaintext&lt;/code&gt; option. The great thing about GitHub Actions is that literally any repo can be an action - as long as it's tagged, I didn't have to wait for a PR to be accepted. I just switched the source of the action to my fork and went on my merry way.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;lint-action&lt;/code&gt; Rubocop linter had two issues that needed to be fixed:

&lt;ul&gt;
&lt;li&gt;We use a shared gem on our private repo to share our styles across our projects. The action doesn't work with &lt;code&gt;bundle exec&lt;/code&gt;. Since we had to use &lt;code&gt;rubocop&lt;/code&gt; in the context of the bundle to use our shared gem, I needed to create a brand new linter called &lt;code&gt;rubocop_bundler&lt;/code&gt; just to add the &lt;code&gt;bundle exec&lt;/code&gt; before the command.&lt;/li&gt;
&lt;li&gt;The current Rubocop linter adds &lt;code&gt;"."&lt;/code&gt; to the end of the command. This is not only unnecessary but forces the linter to run on all files, even if we pass a list of files to it. My fork removed the period and all works well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The action forks fixed all the problems, so now this workflow file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caches the gems, so it almost always runs in less than 1 minute.&lt;/li&gt;
&lt;li&gt;Records all warnings and errors as comments in the PR.&lt;/li&gt;
&lt;li&gt;Auto-fixes fixable warnings with an additional commit in the PR.&lt;/li&gt;
&lt;li&gt;Adds a Check to the Checks pane where we can inspect the offenses in a nice UI.&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3s7qzx1fkgtasxfmakz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3s7qzx1fkgtasxfmakz5.png" alt="Running Checks with Linting"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh20pxl722rxtid9w0qx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh20pxl722rxtid9w0qx0.png" alt="A Workflow Run"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcsjg19ossd75t6nhuqac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcsjg19ossd75t6nhuqac.png" alt="Output of Lint Action"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fokadbznk2q0lorhlz0b0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fokadbznk2q0lorhlz0b0.png" alt="Annotations in Checks"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu9t609xrh1xpsw5poznp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu9t609xrh1xpsw5poznp.png" alt="New Commit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was done with Ruby, but you can use the linter for any language you like!&lt;/p&gt;

&lt;p&gt;Addendum: I've added PRs to both the abovementioned actions, and have gotten a response, so this functionality should be available in the base repos soon, although it might look a bit different. (In addition, see &lt;a href="https://github.com/samuelmeuli/lint-action/issues/1" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; where it mentions that some of the annotations might be clobbered in some cases - we felt this was acceptable.)&lt;/p&gt;

&lt;p&gt;Update March 30: My PRs and issues have been released! The new way to do this (using the official action repos) is as follows:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get file changes&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_file_changes&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trilom/file-changes-action@v1.2.3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Echo file changes&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;echo Changed files: ${{ steps.get_file_changes.outputs.files }}&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run lint&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;samuelmeuli/lint-action@v1.4.0&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;rubocop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;auto_fix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;rubocop_args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-R --fail-level C --display-only-fail-level-offenses ${{ steps.get_file_changes.outputs.files}}&lt;/span&gt;
        &lt;span class="na"&gt;rubocop_command_prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec&lt;/span&gt;



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

&lt;/div&gt;

</description>
      <category>github</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Introducing Deimos: Using Kafka as the Data Backbone for your Architecture</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Fri, 22 Nov 2019 18:18:46 +0000</pubDate>
      <link>https://dev.to/flipp-engineering/introducing-deimos-using-kafka-as-the-data-backbone-for-your-architecture-306c</link>
      <guid>https://dev.to/flipp-engineering/introducing-deimos-using-kafka-as-the-data-backbone-for-your-architecture-306c</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/flipp-engineering/using-kafka-as-a-data-backbone-part-1-1md6"&gt;previous article&lt;/a&gt;, I detailed the microservice pattern of using Kafka as a data backbone for your architecture — Kafka acts as the source of truth and each microservice consumes data from it. This allows each service to be independent and to own only the data it cares about. We also said that there should be some way to marry traditional relational databases like MySQL and Postgres with the Kafka data backbone with a minimum of fuss.&lt;/p&gt;

&lt;p&gt;We left off with a couple of problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ensuring that downstream systems are not tied to your internal data schemas;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixing the transaction problem, where due to errors data can be written to Kafka or your database but not both.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Ruby on Rails — the Database Experts
&lt;/h3&gt;

&lt;p&gt;Here at Flipp, we use Ruby on Rails heavily for systems that talk to databases. Rails’s ActiveRecord package has been in active development for over 13 years, covers a wide range of features and has been battle-tested in thousands of applications. Rails allows us to talk to our database in a consistent way and abstracts most of the underlying details away for us.&lt;/p&gt;

&lt;p&gt;When we started widely using Kafka in our engineering org, we quickly realized we needed a way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Save data from Kafka to our database&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write data to Kafka — either updating state or sending events&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encode/decode data using Avro and the Confluent Schema Registry (a free centralized hub for all our Avro schemas)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Standardize logging and tracing for all our consumers&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our first attempt to send data across systems was to use Kafka Connect. We ran into a number of problems with it, some of which were detailed in the previous article. We decided it was more prudent for us to strike our own way to solve this problem.&lt;/p&gt;

&lt;p&gt;We started off with a shared library we called FlippRubyKafka in late 2017. Over the last two years, this library has grown, added features and fixed bugs and has powered over a dozen of our core microservices. We are happy to announce that this project, now called &lt;a href="https://github.com/flipp-oss/deimos/" rel="noopener noreferrer"&gt;Deimos&lt;/a&gt;, is now open source!&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%2Fbx74jf0bz2rth5ey4vou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbx74jf0bz2rth5ey4vou.png" alt="Deimos logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deimos Features
&lt;/h3&gt;

&lt;p&gt;Deimos is firmly designed to cut down as much as possible on boilerplate and plumbing code. It is focused entirely on the use case of Kafka as a data backbone with Avro composing the structure of the data.&lt;/p&gt;

&lt;p&gt;Deimos is built on a previous, smaller library called &lt;a href="https://github.com/phobos/phobos" rel="noopener noreferrer"&gt;Phobos&lt;/a&gt; (&lt;a href="https://en.wikipedia.org/wiki/Deimos_(moon)" rel="noopener noreferrer"&gt;hence the name&lt;/a&gt;), which in turn is built on &lt;a href="https://github.com/zendesk/ruby-kafka" rel="noopener noreferrer"&gt;RubyKafka&lt;/a&gt;, a pure Ruby implementation of the Kafka client.&lt;/p&gt;

&lt;p&gt;Note that Deimos assumes that you have a copy of the &lt;a href="https://hub.docker.com/r/confluentinc/cp-schema-registry/" rel="noopener noreferrer"&gt;Confluent Schema Registry&lt;/a&gt; running. If you’ve already got Kafka and Zookeeper up then all you need to do is deploy the Docker container to the cloud provider of your choice and get a reference to its URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Producers and Consumers
&lt;/h3&gt;

&lt;p&gt;You can use Deimos simply as a way to tie Avro and Kafka together, and it works really well even if you’re not using Rails! Both producers and consumers allow your business logic to deal exclusively with Ruby hashes — they will automatically be encoded to and decoded from Avro via the schema registry before interacting with Kafka.&lt;/p&gt;

&lt;p&gt;Because Avro is a central tenet of Deimos, all producers and consumers must specify which Avro schema and namespace they will use while consuming or producing. In addition, they need to specify how they handle keys for these messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The default, and recommended, way to handle keys is to Avro-encode them. This allows for any downstream systems that use static types (e.g. those written in Java or Scala, which have a large ecosystem related to Kafka) to easily decode them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can also indicate that your topic has no keys (e.g. event topics should &lt;strong&gt;not&lt;/strong&gt; have keys as you want to keep all events, not just the last updated state).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, you can leave your keys as plaintext, although this is not recommended.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When using Avro-encoded keys, you can provide a separate key schema, or you can have Deimos auto-magically create one for you from your value schema, by specifying a field (usually “id”) to extract from it.&lt;/p&gt;

&lt;p&gt;Sample producer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyProducer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Deimos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Producer&lt;/span&gt;

      &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="s1"&gt;'com.deimos.my-app-special'&lt;/span&gt;
      &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="s1"&gt;'MyApp.MyTopic'&lt;/span&gt;
      &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s1"&gt;'MySchema'&lt;/span&gt; &lt;span class="c1"&gt;# will search in the configured path&lt;/span&gt;
      &lt;span class="n"&gt;key_config&lt;/span&gt; &lt;span class="ss"&gt;field: &lt;/span&gt;&lt;span class="s1"&gt;'key_field'&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_some_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;an_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;'some-key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;an_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'some-key2'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;an_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'key_field'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;an_object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;my_field&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample consumer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyConsumer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Deimos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Consumer&lt;/span&gt;

      &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s1"&gt;'MySchema'&lt;/span&gt;
      &lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="s1"&gt;'com.my-namespace'&lt;/span&gt;
      &lt;span class="n"&gt;key_config&lt;/span&gt; &lt;span class="ss"&gt;field: :my_id&lt;/span&gt; 

     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="no"&gt;MyHandlerClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do_something_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:my_field&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key&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;All schemas are automatically registered in the schema registry using the topic name (so if you produce to MyTopic, your schemas are automatically registered to MyTopic-keyand MyTopic-value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Producing to Kafka
&lt;/h3&gt;

&lt;p&gt;Deimos producers always produce to a given topic. Producers have a number of convenience features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You will get an automatically generated timestamp (current time) and message_id (uuid) in your payload, if they are present in the schema. This helps immensely when debugging messages across systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Types will be coerced before saving, so if your schema expects a string but you give it an int, you don’t need to worry about crashes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Metrics are sent allowing you to track the messages you send per topic and partition, as well as tracing enabled to determine if encoding your message is slow.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consuming from Kafka
&lt;/h3&gt;

&lt;p&gt;As with Phobos, Deimos creates a separate thread per consumer, where each consumer listens to a single topic. Metrics and tracing are used to track the number of messages consumed as well as how long they take to process, and there is a special “lag reporter” that sends metrics on the lag for each topic/partition being listened to.&lt;/p&gt;

&lt;p&gt;Consumers will “swallow” errors by default (relying on tracing to handle alerting in this case) and move on, to prevent individual bad messages from blocking your consumer forever. You can change this behavior or define specific errors or situations where you would not want the consumer to continue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with Databases
&lt;/h3&gt;

&lt;p&gt;Although just including Avro will unlock a lot of potential, Deimos really starts to shine once you hook it up to a database via ActiveRecord. As with Avro, there are ActiveRecord versions of both producers and consumers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ActiveRecordConsumers essentially act as a “sink” — dumping a topic into a database table based on some business logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ActiveRecordProducers act as a “source” — taking in an ActiveRecord object and automatically turning it into a payload to be encoded and sent to Kafka.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sample ActiveRecordCosumer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyConsumer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Deimos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActiveRecordConsumer&lt;/span&gt;

      &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s1"&gt;'MySchema'&lt;/span&gt;
      &lt;span class="n"&gt;key_config&lt;/span&gt; &lt;span class="ss"&gt;field: &lt;/span&gt;&lt;span class="s1"&gt;'my_field'&lt;/span&gt;
      &lt;span class="n"&gt;record_class&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;

      &lt;span class="c1"&gt;# Optional override to change the attributes of the record before &lt;/span&gt;
      &lt;span class="c1"&gt;# they are saved.&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_attributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:some_field&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'some_value'&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;Sample ActiveRecordProducer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyProducer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Deimos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActiveRecordProducer&lt;/span&gt;

      &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="s1"&gt;'MyApp.MyTopic'&lt;/span&gt;
      &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s1"&gt;'MySchema'&lt;/span&gt;
      &lt;span class="n"&gt;key_config&lt;/span&gt; &lt;span class="ss"&gt;field: &lt;/span&gt;&lt;span class="s1"&gt;'my_field'&lt;/span&gt;
      &lt;span class="n"&gt;record_class&lt;/span&gt; &lt;span class="no"&gt;Widget&lt;/span&gt;

      &lt;span class="c1"&gt;# Optional override to change the default payload calculated from the record.&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:assoc_key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some_association&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assoc_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;MyProducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_events&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;Widget&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;foo: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;Widget&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;foo: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to the producer, Deimos also provides a mixin you can add to your ActiveRecord objects which will *automatically *send messages whenever your records are created, modified or destroyed (as long as you don’t use mass operations like update_all or import).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Widget&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
      &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Deimos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;KafkaSource&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kafka_producers&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;MyActiveRecordProducer&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;h3&gt;
  
  
  Database Backend
&lt;/h3&gt;

&lt;p&gt;So we’ve got Deimos sending your database records out to Kafka and receiving messages and dumping them in. So far so good. But let’s take this one step further. In the &lt;a href="https://medium.com/flippengineering/using-kafka-as-a-data-backbone-part-1-8316ab7db5e0" rel="noopener noreferrer"&gt;previous article&lt;/a&gt; we discussed some of the issues with having both databases and Kafka in the same application — the problem of &lt;strong&gt;transactions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can read that article for more, but in a nutshell, we want a guarantee that every record written to the database has a corresponding Kafka message, and every Kafka message is written to the database.&lt;/p&gt;

&lt;p&gt;Deimos achieves this with the &lt;strong&gt;database backend&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This feature transparently replaces the inline sending of Kafka messages to instead save them to a table in the database. There is then a separate process that reads the messages off this table and sends them off in batches.&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%2Fp39a8u7jelcu7jgj2laq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fp39a8u7jelcu7jgj2laq.png" alt="Top: Naive Kafka. Bottom: DB Backend pattern."&gt;&lt;/a&gt;)&lt;/p&gt;
Top: Naive Kafka. Bottom: DB Backend pattern.



&lt;p&gt;Enabling this feature is incredibly simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Generate the migration to add the table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set the publish_backend configuration setting to :db .&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the DB Producer in some way (forked process, thread, rake task) via Deimos.run_db_backend!.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also known as the &lt;a href="https://microservices.io/patterns/data/transactional-outbox.html" rel="noopener noreferrer"&gt;transactional outbox pattern&lt;/a&gt; and achieves full transactional integrity. All messages (whether they represent changes to objects or events) are part of the same transaction and will only be saved if the transaction commits. If it rolls back, all messages will also roll back and not be sent.&lt;/p&gt;

&lt;p&gt;Calling code doesn’t even need to know that the DB backend is turned on. You call the publish method on your producer the same way — instead of sending the message to Kafka directly, it will encode it and save it as a message in the database.&lt;/p&gt;

&lt;p&gt;This feature is currently powering our largest producer of data and our production metrics show being able to send 4,000–6,000 messages per second with a single thread.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Helpers
&lt;/h3&gt;

&lt;p&gt;Deimos comes with a robust suite of RSpec test helpers to allow you to test your business logic without having to actually set up Kafka and the schema registry for local testing.&lt;/p&gt;

&lt;p&gt;Just include the TestHelpers module in your specs and automatically stub out all loaded producers and consumers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Deimos&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestHelpers&lt;/span&gt;
      &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;stub_producers_and_consumers!&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumers can be tested by passing a hash and the consumer class into a test method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;test_consume_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;WidgetConsumer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;5&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;"Darth"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;call_original: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Producers can be tested by just sending messages normally and using the have_sent matcher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'widget-topic'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;not_to&lt;/span&gt; &lt;span class="n"&gt;have_sent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anything&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Widget&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;:my_attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'widget-topic'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have_sent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash_including&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_attr&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What’s Next
&lt;/h3&gt;

&lt;p&gt;We’re just getting started with Deimos. We’d love to hear your use cases and feature requests, and we’ve got a &lt;a href="https://github.com/flipp-oss/deimos/issues" rel="noopener noreferrer"&gt;bunch of our own&lt;/a&gt; to start with and plenty of improving to do. We welcome your contributions and we’re excited to see how far this will go!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>kafka</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Using Kafka as a Data Backbone: Part 1</title>
      <dc:creator>Daniel Orner</dc:creator>
      <pubDate>Fri, 22 Nov 2019 18:10:47 +0000</pubDate>
      <link>https://dev.to/flipp-engineering/using-kafka-as-a-data-backbone-part-1-1md6</link>
      <guid>https://dev.to/flipp-engineering/using-kafka-as-a-data-backbone-part-1-1md6</guid>
      <description>&lt;p&gt;Apache Kafka, a distributed, asynchronous streaming platform, has exploded in popularity over the last few years. It boasts a number of advantages, including fault-tolerance, availability, reliability and scalability, and is being used by hundreds of companies ranging from tiny startups to enormous companies like PayPal and Twitter.&lt;/p&gt;

&lt;p&gt;In this article, I’ll describe how Kafka can be used to act as the data backbone for your microservice architecture, and provide a host of advantages while being able to solve many of the inherent disadvantages that come with that pattern. I’ll also cruelly hint at an awesome open-source toolkit we built that solves a big part of this but which will have an entire Part 2 dedicated to it. 😈&lt;/p&gt;

&lt;h3&gt;
  
  
  Event-Driven Microservices
&lt;/h3&gt;

&lt;p&gt;First off — the use case we are describing here is for an ecosystem largely consisting of &lt;strong&gt;event-driven microservices&lt;/strong&gt;. It’s beyond the scope of this article to dive deep into this pattern, but in a nutshell, we want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Decrease coupling between features and services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow different teams to work independently on unrelated features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow services to scale independently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow services to be deployed independently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enforce a strong contract via technology (instead of convention) so we can be largely confident in &lt;strong&gt;contract testing&lt;/strong&gt; instead of &lt;strong&gt;integration testing&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a number of challenges with this paradigm, though — in this article I’ll describe one way to use Apache Kafka to solve some of those challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Data Backbone
&lt;/h3&gt;

&lt;p&gt;In order for our services to be able to do their jobs, they will have to do one of two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Have all the data it needs to do its job, OR&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be able to ask some other service in real-time for the data it needs to do its job.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Option 2 is the one mostly used by HTTP REST services. Unfortunately, this violates some of our goals above, notably decreased coupling and independence, since service B needs to know how to reach service A, and if service A goes down, service B now needs to handle that case.&lt;/p&gt;

&lt;p&gt;Instead, our pattern is to &lt;strong&gt;copy all necessary data from service A into service B&lt;/strong&gt;. This way, each service has immediate use of all the data it needs to do its job.&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%2Fj7rg2wiwboba4klfevkh.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%2Fj7rg2wiwboba4klfevkh.jpg" alt="Just put all your data on the backbone!"&gt;&lt;/a&gt;&lt;/p&gt;
Just put all your data on the backbone!



&lt;p&gt;Ignoring the increased use of disk space (which is really cheap nowadays), there are a couple of problems with this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How does service B get service A’s data? Having it talk directly to service A’s database is a no-no, since that violates independence yet again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How can we ensure real-time (or close to real-time) updates of that data?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How can we make sure we don’t miss any updates?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do we communicate the structure of the data (and changes to that structure) without being strongly coupled to the original service?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turns out, Apache Kafka and Apache Avro have some cool features that will help us achieve our goals and neatly bypass most if not all of our challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upsert Messages
&lt;/h3&gt;

&lt;p&gt;Every message in Kafka has a &lt;strong&gt;key&lt;/strong&gt; associated with it. This key by convention does &lt;strong&gt;not&lt;/strong&gt; indicate the ID of the &lt;strong&gt;message&lt;/strong&gt;, but of the &lt;strong&gt;entity **represented by that message. This allows Kafka to contain “upsert” messages, where each message represents the **current state of an entity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kafka itself uses this idea in its compaction algorithms — if compaction is turned on, it will automatically delete all messages but the last one which share the same key. This allows new consumers to simply skip all previous updates of a particular entity and jump straight to its current state. A deletion can be indicated by sending the entity’s key with a null message.&lt;/p&gt;

&lt;p&gt;For example, if you send a message on a Characters topic that says name: "Luke" with a key of 105, then a second message that says name: "Darth" with a key of 105, Kafka will eventually delete the first message and only leave the last, most updated one, giving Mr. 105 the name “Darth”. If you then send a null message with a key of 105, the last message will eventually be replaced with null, indicating Mr. 105 has been deleted (this is known as a “tombstone” message).&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%2Fcyx7nbuay3ye5li8nh0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcyx7nbuay3ye5li8nh0n.png" alt="Key compaction — old data is removed and only latest state remains."&gt;&lt;/a&gt;&lt;/p&gt;
Key compaction — old data is removed and only latest state remains.



&lt;p&gt;It’s a small feature, but it has huge ramifications. It means you can use Kafka as **the data backbone of your system — as a distributed, subscribable source of truth for any type of data. **You can send the current state of any object and downstream systems will know to create, update or delete it as necessary (I explore this fully below).&lt;/p&gt;

&lt;p&gt;This means you can use Kafka to tie together any number of smaller services, each with their own database, as long as those services send the updates of their data to Kafka individually (and Kafka is set to keep those messages around as appropriate).&lt;/p&gt;

&lt;h3&gt;
  
  
  Structuring Your Data
&lt;/h3&gt;

&lt;p&gt;This only solves part of the problem, though. We want to have these messages represent our data for &lt;strong&gt;anyone&lt;/strong&gt; who wants to read it. We don’t want to get into API hell where every downstream system needs to know about the system that generated that data so it can understand its structure. We want true independence.&lt;/p&gt;

&lt;p&gt;What we really want is to impose some kind of schema on our messages so that not only are we sending updated state, we are also enforcing a contract between producers and consumers so that state is &lt;strong&gt;reliable&lt;/strong&gt; and &lt;strong&gt;understandable&lt;/strong&gt;. Also important is that the contract needs to be &lt;strong&gt;flexible&lt;/strong&gt; so that we can update the producer in specific ways without immediately causing all our downstream producers to crash.&lt;/p&gt;

&lt;p&gt;Enter Apache Avro. This specification not only defines a schema language, but also a binary encoding of that language so that downstream systems can deterministically decode the message to the schema.&lt;/p&gt;

&lt;p&gt;Even better, it introduces the concept of “compatible” schemas, meaning that the schemas on the upstream and downstream systems &lt;strong&gt;don’t have to match exactly&lt;/strong&gt; as long as they are compatible. Upstream systems are free to add fields and make changes, as long as those changes are done in a compatible way, and downstream systems don’t need to move to the new schema unless they need the new changes.&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%2Fk2bny4au6a43poqwor5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk2bny4au6a43poqwor5c.png" alt="Example of a compatible change — adding a new field with a default value."&gt;&lt;/a&gt;&lt;/p&gt;
Example of a compatible change — adding a new field with a default value.



&lt;p&gt;The combination of Kafka and Avro provides a &lt;strong&gt;strongly structured, distributed data backbone for a set of microservices&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicating Your Data
&lt;/h3&gt;

&lt;p&gt;But if data is “owned” by Service A, but is needed by Service B, how does it get there? The answer is duplication of data. Service B contains some kind of materialization (in a SQL/NoSQL database, in memory, etc.) of the contents of a Kafka topic. This materialization is by definition &lt;strong&gt;ephemeral.&lt;/strong&gt; At any time, a service should be able to blow away its materialization and reconstruct it from the Kafka topic. &lt;strong&gt;The topic is the source of truth of data — all materializations are a view into it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This allows each service to truly be independent. Because data gets into a service asynchronously, no service has a direct link to any other service. If your Orders service has to know about Users, and your Users service is currently down, the Orders service doesn’t care. Its data might be a bit out of date, but it reads it from its own materialization of that data. In fact, it doesn’t even know that a Users service &lt;strong&gt;exists&lt;/strong&gt; —all it knows is that there is a Users &lt;strong&gt;topic&lt;/strong&gt;.&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%2F1pru13sn0dcuqrrh49da.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1pru13sn0dcuqrrh49da.png" alt="Consuming services get information from their data stores, not directly from the producing service."&gt;&lt;/a&gt;&lt;/p&gt;
Consuming services get information from their data stores, not directly from the producing service.



&lt;p&gt;And because every message in the topic is encoded with the same Avro schema, it has a guarantee of what the contents of that topic look like — and because it provides its own reader schema, another guarantee that it’ll never crash when reading a message (assuming we don’t break the requirement that all messages in a topic are encoded with the same schema).&lt;/p&gt;

&lt;p&gt;Now that’s all well and good. But how do we implement this “source of truth” in Kafka? What does it look like in the real world? More specifically, how does the data get &lt;strong&gt;into&lt;/strong&gt; Kafka in a safe way?&lt;/p&gt;

&lt;h3&gt;
  
  
  Kafka and State — Uneasy Allies
&lt;/h3&gt;

&lt;p&gt;The “ideal” state is one where &lt;strong&gt;all data is written to the topic first, and then read back into the service’s materialization of that topic&lt;/strong&gt;. This means that even for producing services, there is no primary database or other state — the topic is everything.&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%2F4kisr8nptterfq2h8gab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4kisr8nptterfq2h8gab.png" alt="“Ideal” workflow — write to topic, then read it back to the data store."&gt;&lt;/a&gt;&lt;/p&gt;
“Ideal” workflow — write to topic, then read it back to the data store.



&lt;p&gt;In practice, this is hard to swallow. Confluent, who is the leading company in the Kafka business, provides some helpful tooling for JVM languages around this pattern (e.g. &lt;a href="https://docs.confluent.io/current/streams/concepts.html#ktable" rel="noopener noreferrer"&gt;KTables&lt;/a&gt; and &lt;a href="https://www.confluent.io/product/ksql/" rel="noopener noreferrer"&gt;KSQL&lt;/a&gt;), but it all masks the point that &lt;strong&gt;Kafka is always asynchronous — and sometimes synchronous things need to happen.&lt;/strong&gt; Particularly, anything that’s user-facing will often need to render the most up-to-date state back to the user. There are a number of tactics to allow the pattern to still work in this case (add a throbber / Ajax request, use some kind of cache, etc.) but they all introduce a whole whack of complexity which honestly isn’t really needed.&lt;/p&gt;

&lt;p&gt;What’s more, using Kafka as your single state is simply not always the best option. Relational databases are decades old, and there are hundreds if not thousands of tools and frameworks built around them. They are well understood, extremely performant for most use cases, and incredibly good at what they do. Most web frameworks assume some kind of transaction-enabled, SQL-based database to do what they need to do. We should be able to leverage this standardized technology &lt;strong&gt;and&lt;/strong&gt; the new hotness of Kafka at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Databases and Kafka
&lt;/h3&gt;

&lt;p&gt;There are two current leaders in the field of marrying databases and Kafka: Confluent’s &lt;a href="https://docs.confluent.io/current/connect/index.html" rel="noopener noreferrer"&gt;Kafka Connect&lt;/a&gt; (which polls tables in the database and turns them into Avro-encoded messages), and &lt;a href="https://debezium.io/" rel="noopener noreferrer"&gt;Debezium&lt;/a&gt; (which tails the binary log of your database). Both are used pretty extensively, but they each have downsides. Kafka Connect has no way of sending deletions, since it polls the database, and deleted rows aren’t in the database any more. Debezium is heavily tied to your internal database schema (so is Kafka Connect by default, but you can write custom connectors to get around that).&lt;/p&gt;

&lt;p&gt;So unfortunately, neither of these solutions gives you the whole picture without a bunch of pain points.&lt;/p&gt;

&lt;p&gt;In an ideal world, your database schema is perfect! Everyone will want every column on your table and nothing in there is confusing in any way.&lt;/p&gt;

&lt;p&gt;In the real world, you need your database schema to be &lt;strong&gt;internal&lt;/strong&gt; and shielded from your consumers. You’re going to have implementation details that just aren’t relevant to anyone else. You may also want to combine multiple tables into a single event without having to write a separate joiner service downstream or being forced to write views for every table. You want to publish &lt;strong&gt;external-facing&lt;/strong&gt; messages which use your data but &lt;strong&gt;not&lt;/strong&gt; your schema.&lt;/p&gt;

&lt;p&gt;That’s problem 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Transaction Gotcha
&lt;/h3&gt;

&lt;p&gt;We’ve been talking about sending your database state around — but let’s not forget that Kafka can and should still be used to send &lt;strong&gt;events&lt;/strong&gt;. Most services that have state will still want to send events down the Kafka pipe. Keeping this in mind introduces one more sneaky issue: Transactions.&lt;/p&gt;

&lt;p&gt;When writing data to a database, the write can fail (due to deadlocks, timeouts, etc.). Writes can also fail to Kafka (downtime, timeouts, broker issues). If one succeeds and not the other, you’ve now broken your guarantees of “single source of truth”.&lt;/p&gt;

&lt;p&gt;Let’s say you’re writing your Character to a table, and sending a “Character Requested” event to a separate Kafka topic (perhaps with user and timestamp information). If you do this all in one transaction, the Character write can succeed but the sending the event can fail (meaning you know about the new character but the event didn’t go out), or the event can succeed but the database write can fail (meaning the event went out but it’s referencing a character that was never written).&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%2Fse0koghtic09sehctn9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fse0koghtic09sehctn9i.png" alt="Write works to Kafka but not the database — Luke is in the topic but not the service’s own DB."&gt;&lt;/a&gt;&lt;/p&gt;
Write works to Kafka but not the database — Luke is in the topic but not the service’s own DB.



&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%2F13xv11qt9hnzwd6u0nfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F13xv11qt9hnzwd6u0nfg.png" alt="Write works to the DB but not Kafka — downstream systems will not get the new write."&gt;&lt;/a&gt;&lt;/p&gt;
Write works to the DB but not Kafka — downstream systems will not get the new write.



&lt;p&gt;That’s problem 2.&lt;/p&gt;

&lt;p&gt;So how do we do it? How do we marry our database and Kafka in a way that keeps our data &lt;strong&gt;eventually consistent&lt;/strong&gt;, allows us to use all our lovely relational database tools for our web apps, provides the advantages of an Event-Driven Microservices architecture, and even lets us break up long-running monoliths?&lt;/p&gt;

&lt;p&gt;Go forward and read &lt;a href="https://dev.to/flipp-engineering/introducing-deimos-using-kafka-as-the-data-backbone-for-your-architecture-306c"&gt;Part 2&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>ruby</category>
      <category>rails</category>
      <category>kafka</category>
    </item>
  </channel>
</rss>
