<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andrew Stuntz</title>
    <description>The latest articles on Forem by Andrew Stuntz (@drews256).</description>
    <link>https://forem.com/drews256</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%2F17733%2Feb13b848-0035-45c3-8397-310166992e53.jpeg</url>
      <title>Forem: Andrew Stuntz</title>
      <link>https://forem.com/drews256</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/drews256"/>
    <language>en</language>
    <item>
      <title>How long does it take for your test suite to run?</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Wed, 02 Dec 2020 22:45:07 +0000</pubDate>
      <link>https://forem.com/drews256/how-long-does-it-take-for-your-test-suite-to-run-48h4</link>
      <guid>https://forem.com/drews256/how-long-does-it-take-for-your-test-suite-to-run-48h4</guid>
      <description>&lt;p&gt;I think long-running test suites are the bane of the development flow. They cause friction in what is already a fairly fraught process and often times don't even lead you to the correct results.&lt;/p&gt;

&lt;p&gt;How long does it take your test suite to run? How many tests do you have?&lt;/p&gt;

&lt;p&gt;Do you run those tests in the cloud? How often do you run tests locally?&lt;/p&gt;

&lt;p&gt;Let me know. How long does it take for your test suite to run? Does it take too long?&lt;/p&gt;

</description>
      <category>testing</category>
      <category>discuss</category>
      <category>rails</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What are the pros and cons of StimulusJS and AlpineJS?</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Fri, 16 Oct 2020 17:37:21 +0000</pubDate>
      <link>https://forem.com/drews256/what-are-the-pros-and-cons-of-stimulusjs-and-alpinejs-2f7i</link>
      <guid>https://forem.com/drews256/what-are-the-pros-and-cons-of-stimulusjs-and-alpinejs-2f7i</guid>
      <description>&lt;p&gt;I've been using StimulusJS heavily on two rails apps I have been working on and honestly, I'm loving it. But, I have been reading and hearing a lot lately about AlpineJS. What are the pros and cons? Should I be using AlpineJS instead of StimulusJS? &lt;/p&gt;

</description>
      <category>discuss</category>
      <category>rails</category>
      <category>alpinejs</category>
      <category>stimulus</category>
    </item>
    <item>
      <title>A Future for Rails: StimulusReflex</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Fri, 21 Aug 2020 22:53:33 +0000</pubDate>
      <link>https://forem.com/drews256/a-future-for-rails-stimulusreflex-48kb</link>
      <guid>https://forem.com/drews256/a-future-for-rails-stimulusreflex-48kb</guid>
      <description>&lt;p&gt;If you've been in the rails community for any length of time, you've probably heard about the desire to stay away from JavaScript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I like JavaScript, but I don't like it that much."&lt;br&gt;
David Heinemeier Hanson, Creator of Ruby on Rails&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's easy to say that most Rails developers are anxious around JavaScript. This led to the rise of Coffeescript, Sprockets, and a whole suite of tools to allow Ruby on Rails developers to build amazing applications without having to write JavaScript.&lt;/p&gt;

&lt;p&gt;As Rails continues its path to being a larger and more complete framework, "batteries included," there have been further shifts in the community. Included with Rails is now a more modern JavaScript packager (though Sprockets is still around if you want to use it). DHH also introduced us to a whole new JavaScript Framework called Stimulus. It allows you to use modern Javascript on your Rails projects without having to go outside the "rails way".&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%2Fr0g1wjxlxxqovushi1o2.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr0g1wjxlxxqovushi1o2.jpg" alt="two developers collaborating on project in Phoenix LiveView"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phoenix, Elixir, and LiveView
&lt;/h2&gt;

&lt;p&gt;As other communities have popped up to build "rails inspired" frameworks, many of those frameworks emerged as formidable competitors to single-page applications. In the Phoenix and Elixir community, Chris McChord introduced the world to LiveView, a whole new paradigm for serving and updating HTML from your backend.&lt;/p&gt;

&lt;p&gt;At its core, LiveView is able to leverage Websockets and update the Document Object Model (DOM) based on responses from the WebSocket connection. These frameworks are fast enough to feel "real-time".&lt;/p&gt;

&lt;p&gt;The promise of LiveView is a return to the early days of Ruby on Rails, where we didn't have to worry so much about JavaScript and we could happily focus on our business logic. Everything else to build a robust CRUD application was taken care of using "rails magic."&lt;/p&gt;

&lt;p&gt;Some people in the Rails community have jumped ship to the Elixir/Phoenix community. To be fair, I personally think that for most problems, Phoenix is an amazing solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can Rails still compete?
&lt;/h2&gt;

&lt;p&gt;Rails is nothing to sneeze at and the Rails community has taken steps towards being real-time as well. DHH recently tweeted and to let folks in the community know that much of Hey was built with some sort of frontend updating system that builds on Stimulus, Turbolinks, and the "rails way". I'm excited to hear more.&lt;/p&gt;

&lt;p&gt;There have been other efforts to push Rails in the "real-time" direction as well. Recently I came upon StimulusReflex which builds on the Stimulus framework and a gem called CableReady that enables you to build "reflexes" that work much like LiveView does in Phoenix: sending DOM updates over a WebSocket to update the UI.&lt;/p&gt;

&lt;p&gt;StimulusReflex could be a possible future for Rails development. I'd like to share my experience building an application with StimulusReflex.&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%2F8ikpykrz8xw3t8ot1p9g.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%2F8ikpykrz8xw3t8ot1p9g.png" alt="rails chat app example on MacBook pro screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a chat app with StimulusReflex
&lt;/h2&gt;

&lt;p&gt;I recently wrote a chat application that is fast, responsive, and was simple to build while leveraging StimulusReflex. I also wrote a total of 24 lines of JavaScript - you can't beat that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;It's easy to get started with StimulusReflex. It's a gem and can be installed in your Rails app by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle add stimulus_reflex&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To get it up and running, simply run the install scripts:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bundle exec rails stimulus_reflex:install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This generates a couple of new starter files and directories. Including the &lt;code&gt;app/reflexes&lt;/code&gt; directory. You'll see an &lt;code&gt;example_reflex.rb&lt;/code&gt; and an &lt;code&gt;application_reflex.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You also get some JavaScript out of the box, and you'll have a newly created &lt;code&gt;app/javascript/controllers/application_controller.rb&lt;/code&gt; and an &lt;code&gt;app/javascript/controllers/example_controller.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reflexes are where the magic happens. According to the documentation, a "reflex" is a "full, round-trip life-cycle of a StimulusReflex operation - from client to server and back again. The JavaScript controllers are Stimulus Controllers and StimulusReflex is designed to hook right into Stimulus and other Rails paradigms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Up and Running
&lt;/h3&gt;

&lt;p&gt;Now that we have StimulusReflex setup, let's go ahead and start setting up our chat application. I'm going to make some assumptions.&lt;/p&gt;

&lt;p&gt;You have a Rails application that is using Devise.&lt;/p&gt;

&lt;p&gt;You have a Message model that includes a message and user_id attribute.&lt;/p&gt;

&lt;p&gt;Also, I won't go into the minutiae of writing the views or the styling for the application. If you're curious, I'll link out to the entire code base for you to see what I wrote.&lt;/p&gt;

&lt;p&gt;The first thing I did was build out a link to a view that will render our reflex enabled HTML. There is no special controller needed here, I called mine the ChatController and linked to it from my routes.rb as &lt;code&gt;get /chat to: "chat#index"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here I expose my messages to the view:&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;ChatController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="n"&gt;skip_authorization_check&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;locals: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;messages: &lt;/span&gt;&lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;created_at: :desc&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;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then consume those messages in the chat index view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight slim"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.flex.flex-col.mx-4&lt;/span&gt;
  &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.container.flex.flex-col-reverse.overflow-scroll.bg-white.mt-4.rounded&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now have a nice list of messages in your view - easy peasy.&lt;/p&gt;

&lt;p&gt;The next step is to add in some Stimulus to create messages. We're just going to get the button click working with Stimulus right now. I added a button with a Stimulus data attribute to &lt;code&gt;create_message&lt;/code&gt; in the Chat controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight slim"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.flex.flex-col.mx-4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;data-controller&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'chat'&lt;/span&gt;
  &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.container.flex.flex-col-reverse.overflow-scroll.bg-white.mt-4.rounded&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
  &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.mb-4&lt;/span&gt;
    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form_with&lt;/span&gt; &lt;span class="ss"&gt;model: &lt;/span&gt;&lt;span class="vi"&gt;@message&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;form&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.mb-4.rounded-md&lt;/span&gt;
        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;
        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_field&lt;/span&gt; &lt;span class="ss"&gt;:message&lt;/span&gt;
        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt; &lt;span class="s2"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;action: &lt;/span&gt;&lt;span class="s2"&gt;"click-&amp;gt;chat#create_message"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;app/javascript/controllers/chat_controller.js&lt;/code&gt; you should create a create_message handler that handles the click event from your Message button.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stimulus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;StimulusReflex&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stimulus_reflex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;create_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you click the Message button you should see 'Clicked' in the JavaScript console. I love Stimulus that is easy. You don't actually have to use Stimulus here - you can add data attributes to your HTML that will trigger reflex actions straight from your HTML.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.stimulusreflex.com/reflexes#declaring-a-reflex-in-html-with-data-attributes" rel="noopener noreferrer"&gt;See the note here&lt;/a&gt; in the StimulusReflex docs.&lt;/p&gt;

&lt;p&gt;To connect this to our Reflex we need to import the StimulusReflex module and register it in our Controller.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stimulus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;StimulusReflex&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stimulus_reflex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;StimulusReflex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;create_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stimulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat#create_message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="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;Now when we click the button you should see a big fat error saying that the ChatReflex module does not exist. Now let's create the ChatReflex and handle our &lt;code&gt;create_message&lt;/code&gt; action.&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;ChatReflex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StimulusReflex&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Reflex&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :connection&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_message&lt;/span&gt;
    &lt;span class="c1"&gt;# Create the message&lt;/span&gt;
    &lt;span class="no"&gt;Message&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;message: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;current_user&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="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;See the connection delegation at the top of the class? We need to be sure to expose that to our connection.&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;ApplicationCable&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Connection&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
    &lt;span class="n"&gt;identified_by&lt;/span&gt; &lt;span class="ss"&gt;:current_user&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&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;current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_verified_user&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_verified_user&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"warden"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;
        &lt;span class="n"&gt;current_user&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;reject_unauthorized_connection&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We identify our Chat reflex connection by the &lt;code&gt;current_user&lt;/code&gt; which uses the &lt;code&gt;current_user.id&lt;/code&gt; to separate connections. We also add a layer of Authentication here that allows us to pick up our user from Devise when we connect to our WebSocket.&lt;/p&gt;

&lt;p&gt;After this is created, when you click your Message button, you should now see the message returned back to view and update in real-time. It's really fast, and also super easy.&lt;/p&gt;

&lt;p&gt;Summary of where we are right now:&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%2Fmsmpif68v55rvad1e6zw.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%2Fmsmpif68v55rvad1e6zw.png" alt="summary flow chart"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Bringing it all together
&lt;/h3&gt;

&lt;p&gt;So now you can connect two users to the chat room and attempt to chat with one another. But there is a small problem - you only get the other users' messages when you send a message or refresh the page. That's not very real-time at all.&lt;/p&gt;

&lt;p&gt;Currently, the view is only re-rendered and broadcast to the &lt;code&gt;current_user&lt;/code&gt; on that connection. But, we can leverage ActionCable to tell StimulusReflex that there are new messages that need to be rendered. Easy enough.&lt;/p&gt;

&lt;p&gt;To achieve this, we spin up a new ActionCable channel. I called it the ChatChannel.&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;ChatChannel&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationCable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Channel&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subscribed&lt;/span&gt;
    &lt;span class="n"&gt;stream_from&lt;/span&gt; &lt;span class="s2"&gt;"chat_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:room&lt;/span&gt;&lt;span class="p"&gt;]&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;‍&lt;br&gt;
This allows us to connect to a Chat room outside of the StimulusReflex paradigm and we can broadcast back up this channel that the users that are attached need to get the new messages.&lt;/p&gt;

&lt;p&gt;To accomplish this, we add some JavaScript to initiate that connection:&lt;/p&gt;

&lt;p&gt;In your Stimulus chat controller the initializer connects to the &lt;code&gt;ChatChannel&lt;/code&gt; and then binds a function to the received callback of the ActionCable subscription.&lt;br&gt;
‍&lt;/p&gt;

&lt;p&gt;It looks like:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stimulus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;StimulusReflex&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stimulus_reflex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;consumer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../channels/consumer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;StimulusReflex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;initialize&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 need to know when there are new messages that have been created by other users&lt;/span&gt;
    &lt;span class="nx"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&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="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ChatChannel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public_room&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_cableReceived&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;create_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stimulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat#create_message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;_cableReceived&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stimulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat#update_messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we receive a message (any message currently) we trigger the &lt;code&gt;_cableReceived&lt;/code&gt; callback which will stimulate the &lt;code&gt;Chat#update_messages&lt;/code&gt; actions.&lt;/p&gt;

&lt;p&gt;Add an &lt;code&gt;update_messages&lt;/code&gt; action to the Chat reflex and then broadcast a message when you create the Message in the &lt;code&gt;create_message&lt;/code&gt; action. So, now when any user creates a new Message, we will rebroadcast that message out to anyone that is connected to the &lt;code&gt;chat_public_room&lt;/code&gt; connection.&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;ChatReflex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StimulusReflex&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Reflex&lt;/span&gt;
  &lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="ss"&gt;:current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: :connection&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_message&lt;/span&gt;
    &lt;span class="c1"&gt;# Create the message&lt;/span&gt;
    &lt;span class="no"&gt;Message&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;message: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;user_id: &lt;/span&gt;&lt;span class="n"&gt;current_user&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="c1"&gt;# Broadcast that everyone on this channel should get messages&lt;/span&gt;
    &lt;span class="no"&gt;ActionCable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;"chat_public_room"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="s1"&gt;'get_messages'&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;update_messages&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;Since the view gets re-rendered from the &lt;code&gt;ChatController&lt;/code&gt;, we don't actually have to do anything with the messages in the Reflex action. We get to treat the view just like we would have in a Controller Action.&lt;/p&gt;

&lt;p&gt;Now you should be able to attach two users and chat back and forth with one another.&lt;/p&gt;

&lt;h3&gt;
  
  
  The flow through the app looks like:
&lt;/h3&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%2F5a16tlo0qmsfwjtckdyy.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%2F5a16tlo0qmsfwjtckdyy.png" alt="flow through the app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;‍&lt;br&gt;
So we short circuit the button to force stimulus to trigger the Reflex to get everyone's chat's to refresh when a new Message is created.&lt;/p&gt;

&lt;p&gt;It's simple and very powerful. The tools that build this application are quite well known and StimulusReflex is built on well-known Rails gems and tools including, Stimulus, CableReady, and ActionCable.&lt;/p&gt;

&lt;p&gt;I'm excited to continue exploring how to leverage StimulusReflex in apps we build at Headway, as well as other solutions in this vein including LiveView on Phoenix and Elixir. I believe it will reduce the time we all spend writing single-page applications without losing the real-time feel.&lt;/p&gt;

&lt;h3&gt;
  
  
  The results so far
&lt;/h3&gt;

&lt;p&gt;Below is the final version of my quick and dirty StimlusReflex chat application.&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%2Fg54z2216ah3q2uxc9sz2.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%2Fg54z2216ah3q2uxc9sz2.png" alt="final chat app results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;I remember building a chat application when ActionCable first launched and thinking the set up was lengthy and took some work. WebSockets were mysterious to me too. StimulusReflex does a great job in hiding that complexity and makes your Rails application feel even more like an SPA. There is such little JavaScript in this version it's not even funny.&lt;/p&gt;

&lt;h3&gt;
  
  
  More opportunities for this chat application are:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add better Authentication and do some work to add presence notifications&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make it so you can create and join any chat room - not just the public room&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get access to the code
&lt;/h2&gt;

&lt;p&gt;What would you add to my chat application next? You can see the final version of the code for this walkthrough here on GitHub. I added some styling and made it look a bit like a messenger application.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>web</category>
      <category>stimulus</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Tips for Managing Projects with Remote Development Teams</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Fri, 08 May 2020 17:17:51 +0000</pubDate>
      <link>https://forem.com/headwayio/tips-for-managing-projects-with-remote-development-teams-4pam</link>
      <guid>https://forem.com/headwayio/tips-for-managing-projects-with-remote-development-teams-4pam</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally posted on the Headway blog. Visit us at &lt;a href="https://www.headway.io" rel="noopener noreferrer"&gt;headway.io&lt;/a&gt; to see how we're making waves.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article, we share guidelines and advice for remote software development teams to more effectively manage and complete projects. These guidelines help ensure that remote meetings, communication, and team projects are productive, even when they happen remotely.&lt;/p&gt;

&lt;p&gt;In March of 2020, Headway held a live stream with our team to discuss how product teams can &lt;a href="https://headway.io/events/working-as-a-remote-software-developer" rel="noopener noreferrer"&gt;work remotely successfully&lt;/a&gt;. We planned on having this online event long before the world migrated to mostly online work environments in the spring of 2020.&lt;/p&gt;

&lt;p&gt;Headway's remote-first approach to managing teams, holding meetings, and running projects eased our transition. We hope that sharing our experiences and advice will help others build best practices for creating more impactful remote teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'll cover
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Best practices for remote meetings&lt;/li&gt;
&lt;li&gt;Remote project management and task prioritization&lt;/li&gt;
&lt;li&gt;Remote communication tools&lt;/li&gt;
&lt;li&gt;Remote collaboration tools&lt;/li&gt;
&lt;li&gt;Leading and managing your team&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%2Ft6z01dc7xevwqvybb73d.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft6z01dc7xevwqvybb73d.jpg" alt="All Hands Meeting Via Zoom"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices for remote meetings
&lt;/h2&gt;

&lt;p&gt;Due to the nature of remote work, it can be frustrating for remote workers to be involved in meetings that happen "in-house". At Headway, all meetings are remote-friendly - even when there are folks in the office. Remote friendly meetings have a few things in common:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Cameras on
&lt;/h3&gt;

&lt;p&gt;For complex conversations, having a voice conversation is better than using Slack. And having your camera on is even better. Humans are very in tune with body language, and that includes body language over video calls. Encourage your team, and any project partners, to turn their camera on. This builds trust and improves the overall relationship. And better relationships create better products.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Headsets on
&lt;/h3&gt;

&lt;p&gt;Clear audio creates clear communication. The best calls happen when everyone has headsets. It results in less delay when talking into the mic and most headsets filter noise around you better than the mics on your laptops.&lt;/p&gt;

&lt;p&gt;Our team uses &lt;a href="https://www.jabra.com/business/office-headsets" rel="noopener noreferrer"&gt;Jabra headsets&lt;/a&gt; to improve audio communication.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Mute when not speaking
&lt;/h3&gt;

&lt;p&gt;This is a great way to prevent any disruptions from your end while others are speaking. It can prevent you from butting in before they finish a thought and prevents everyone from hearing you sneeze or cough at random. This is especially helpful if you are in a busy environment. Whether your kids are at home or you're working from a coffee shop, be kind and mute your microphone when you're not speaking. When in doubt, mute yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Face your camera
&lt;/h3&gt;

&lt;p&gt;In today's age, many of us have multiple monitors - but if you can't see my face, you're probably not going talk to me in the same way. Don't be scared or nervous. It's a little unnerving watching yourself on the camera the first few times, but trust me, you'll get used to it. The more you engage with each other, the stronger your relationships become.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Better lighting
&lt;/h3&gt;

&lt;p&gt;Being seen in the right light is important too. Don't be the creepy person backlit by your window. Try to have light sources in front of you. If there are bright lights behind you, turn them off or reposition your desk in the room so that your face is lit more than your back.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Stay on track
&lt;/h3&gt;

&lt;p&gt;It can be really easy to get into the weeds and talk in circles. Just like any other meeting, hold each other accountable, and respect each other's time. You can do this by having an agreed agenda at the beginning of the meeting or designate someone to help lead the group if they notice the meeting is getting off track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote project management for developers
&lt;/h2&gt;

&lt;p&gt;As a remote-first consultancy, Headway works on a wide variety of projects. We work with new startups, established startups, and enterprise businesses. One of the most challenging things to do with a product team is handling project management. With the right tools, you can make the process easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project and product documentation
&lt;/h3&gt;

&lt;p&gt;Having a collaborative resource for documentation will make a big impact on remote teams. We use &lt;a href="https://www.notion.so/" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; for documenting everything we do. It keeps everything we need accessible, secure, and easy to share.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits of project documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Templates create consistency&lt;/li&gt;
&lt;li&gt;Creates alignment across the team&lt;/li&gt;
&lt;li&gt;Provides a singular source of truth&lt;/li&gt;
&lt;li&gt;Successful project hand-offs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.notion.so/" rel="noopener noreferrer"&gt;Learn more about Notion&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritizing feature development
&lt;/h3&gt;

&lt;p&gt;Headway uses MoSCoW prioritization to manage feature development for products that we build. It allows project stakeholders to clearly understand what work will happen, when it will happen, and why on their product at any given time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn how Headway prioritizes feature work:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://headway.io/blog/prioritizing-your-product-features-with-the-moscow-method" rel="noopener noreferrer"&gt;Prioritizing Your Product Features with the MoSCoW Method&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frs734wxhngk8ejbxucpd.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%2Frs734wxhngk8ejbxucpd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing feature development with Stories on Board
&lt;/h3&gt;

&lt;p&gt;We use a tool called &lt;a href="https://storiesonboard.com/" rel="noopener noreferrer"&gt;Stories On Board&lt;/a&gt; for roadmap prioritization. It helps bring the product, design, and development together and is an excellent compliment to the &lt;a href="https://headway.io/blog/prioritizing-your-product-features-with-the-moscow-method" rel="noopener noreferrer"&gt;MoSCoW method&lt;/a&gt;. It provides a clear view of what is getting done, when it is getting done, and compliments agile development methods.&lt;/p&gt;

&lt;p&gt;This storyboard tool isn't an issue tracker. Translating storyboard cards into usable tickets for a developer takes work. We don't always work with project managers, and often the role of our lead developer or designer includes project management. This is why our tools and consistent process are imperative to our success. Providing consistency for one another is critical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://storiesonboard.com/" rel="noopener noreferrer"&gt;Learn more about Stories On Board&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing development projects tasks
&lt;/h3&gt;

&lt;p&gt;Across all our projects, we decompose storyboard items into more consumable tickets to work in and often use a separate tracker for issue tracking. Success starts with having the right tools to achieve your goals and stay on track. Every project has different needs either because of its nature or because of the client's requirements. Here are the tools we have used to manage tasks as a team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task management tools we recommend
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://guides.github.com/features/issues/" rel="noopener noreferrer"&gt;Github Issues&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asana.com/" rel="noopener noreferrer"&gt;Asana&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.atlassian.com/software/jira" rel="noopener noreferrer"&gt;Jira&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://trello.com/" rel="noopener noreferrer"&gt;Trello&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://usebirdseye.com/" rel="noopener noreferrer"&gt;Birdseye&lt;/a&gt; - &lt;em&gt;We're building it&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand your needs
&lt;/h3&gt;

&lt;p&gt;Our projects use different issue trackers at Headway. Depending on the clients and partners involved, the tools can change. We have used Github issues, Trello, Asana, and Jira to track project work. The important thing here is not which issue tracker you decide to use. It's how you decide to use it.&lt;/p&gt;

&lt;p&gt;It's important to keep issues up to date and as a remote developer. This is because I can't just stop by a PM's office or a teammate's desk to see how things are moving. The place we must look at is the issue tracker.&lt;/p&gt;

&lt;p&gt;We often include it in our Github pull request templates to make sure you check off the ticket (or update the ticket to the appropriate state) in whatever issue tracker you are using. We rely mostly on the following states: "started", "doing", "done" and sometimes we will add in "confirmed" or something else to make sure it passes through QA without any issues.  It's easy to share and see what other folks are doing that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication tools for remote developers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tools we recommend
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Slack&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7p652f7ych466js70r58.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%2F7p652f7ych466js70r58.png" alt="Slack Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use Slack as our main tool for asynchronous communication. It allows us to separate and track conversations for different projects and have quick questions answered with individuals as needed. You can even hop on a quick slack call if needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://slack.com/solutions/remote-work" rel="noopener noreferrer"&gt;Learn how Slack works&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9c4pbzyji8565t6613kd.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%2F9c4pbzyji8565t6613kd.png" alt="Zoom Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zoom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We use zoom when we schedule formal meetings or when we need to go into a deeper conversation about an issue together. We also record meetings to create accountability between project partners and document meetings in case they ever need to be revisited.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zoom.us/meetings" rel="noopener noreferrer"&gt;Learn more about Zoom&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous communication
&lt;/h2&gt;

&lt;p&gt;Struggling to communicate decisions across members of different teams is a shared failure I've seen many times. Keeping each member up to date on what is happening when and where has always been a challenge. It's one place where remote work shines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a source of truth with Slack
&lt;/h3&gt;

&lt;p&gt;Headway uses Slack to facilitate our asynchronous communication. There is no siloed communication about development, design, or process as we all make those decisions by working in Slack. Even if it's easier to have communication outside of Slack or using a Zoom room. Slack is the tool we use to broadcast those decisions.&lt;/p&gt;

&lt;p&gt;When I take a day off, the first thing I do when I return is to read through Slack messages. This allows me to catch up on decisions that were made, work that is inflight, and issues that came up while I was gone. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/pQcN1xO7RdA" rel="noopener noreferrer"&gt;https://youtu.be/pQcN1xO7RdA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/pQcN1xO7RdA" rel="noopener noreferrer"&gt;https://youtu.be/pQcN1xO7RdA&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using channels in Slack
&lt;/h3&gt;

&lt;p&gt;Since you are unable to have one-off conversations or "just stop by" someone's desk, you have to focus on communication in other ways. An asynchronous tool like Slack solves this naturally with channels. Since there is a log of all communication and decisions being made, it's easier to communicate, discuss, and collaborate.&lt;/p&gt;

&lt;p&gt;That means no more email forwards and weird gaps in the conversation.&lt;/p&gt;

&lt;p&gt;When a decision is made in a Slack channel, it not only drives the decision in a way that all teammates can see and chime in on. It also lets everyone read that communication when it makes sense for them as well.&lt;/p&gt;

&lt;p&gt;You no longer have to worry about getting everyone into a meeting or having a face to face conversation with a product owner because as long as they're in the channel, they will see or read about the decisions that have been made. Decisions can and should also be pulled out of Slack and included in project management tools. This provides the historical context you can link to in the slack conversation. This allows everyone to catch up on the project on their schedule and when it's convenient for them. &lt;/p&gt;

&lt;p&gt;Not sure how to begin?&lt;/p&gt;

&lt;p&gt;Check out this article for getting started with Slack channels:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://slackhq.com/how-to-use-slack-channels-to-organize-your-work" rel="noopener noreferrer"&gt;How to use Slack channels to organize your work life&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing notifications and distractions
&lt;/h3&gt;

&lt;p&gt;If you don't manage it correctly, Slack can be overwhelming. It can be very intrusive into your life with constant pinging and constant updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't be scared to turn notifications off.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I usually spend at least half of my day with "Do Not Disturb", on my computer. This allows me to check Slack on my schedule and since it's asynchronous communication, you check for updates when it makes sense for you.&lt;/p&gt;

&lt;p&gt;Another strategy is to turn off notifications unless someone explicitly mentions you in Slack. This can be helpful if you're in a Slack channel that sees heavy use but you're not directly involved. Or if you just don't want notifications every time someone mentions something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote whiteboarding tools and collaboration
&lt;/h2&gt;

&lt;p&gt;Besides communication tools like Slack and Zoom, we use a plethora of other tools to make our experience working together optimal. I often hear that teams moving to work remotely have a hard time communicating when they don't have a whiteboard. There are some great tools to help solve this problem.&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%2Ftxnd8tfu89vw264hxq9f.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%2Ftxnd8tfu89vw264hxq9f.png" alt="Whimsical Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing ideas visually
&lt;/h3&gt;

&lt;p&gt;One big push back many folks have about working remotely is the lack of being able to have "whiteboard sessions" or collaborate with their peers in a visual manner. So-called "whiteboard sessions" are often used to quickly better understand problems, scope, or hard to visualize patterns. As a developer it can be really tough to communicate your thoughts in writing. We have found a few tools that really allow us to communicate visually virtually that have, on occasion, saved the day.&lt;/p&gt;

&lt;h4&gt;
  
  
  Whimsical
&lt;/h4&gt;

&lt;p&gt;From a development standpoint the first tool that I reach for is Whimsical. Whimsical allows you to draw mind maps, wireframes, and flow charts quickly with your team. Whimsical allows you to think and communicate visually without too much friction. Whimsical gets used for architecture, code organization, process management, and quick and dirty UI flows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://whimsical.com/" rel="noopener noreferrer"&gt;Learn more about Whimsical&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Miro
&lt;/h4&gt;

&lt;p&gt;The other tool we reach for at Headway is Miro, Miro is team collaboration software that feels like more like we are collaborating on a whiteboard. It's great for brainstorming, planning, and user story mapping. It's easy to use, fast, and extremely collaborative. Often we'll leave Miro up during meetings just to share notes. Then we can organize those notes together after the meeting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://miro.com/" rel="noopener noreferrer"&gt;Learn more about Miro&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb8oo8hi1p4nf7td6avpj.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%2Fb8oo8hi1p4nf7td6avpj.png" alt="Miro Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Between Miro and Whimsical rarely am I unable to feel like I'm missing out on collaboration without a whiteboard. Using these tools is often more collaborative, no one is hogging a marker, you can't run out of whiteboard space, and there almost infinite ways to move work into different formats for different learning styles. Another benefit is there is no "translating" step. It's already in a digital format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote pair programming tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From an integrated development environment (IDE)
&lt;/h3&gt;

&lt;p&gt;As developers, we use quite a few tools to make remote collaboration with one another an easy experience. The most popular tool we use at Headway for remote pairing is &lt;a href="https://tuple.app/" rel="noopener noreferrer"&gt;Tuple&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc04ox37q96d4jojygdhz.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%2Fc04ox37q96d4jojygdhz.png" alt="Tuple Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tuple is a screen sharing software built for developers. It allows a seamless screen share and has lots of options for the way to interact with your pairs screen. Tuple also has excellent screen annotation tools. It's the best option for pairing when developers are working in an IDE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuple.app/" rel="noopener noreferrer"&gt;Learn more about Tuple&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Without an IDE
&lt;/h3&gt;

&lt;p&gt;If you're working on a remote server or like the idea of working from a remote server and working outside of an IDE (like using Vim) we use wemux. &lt;a href="https://github.com/zolrath/wemux" rel="noopener noreferrer"&gt;Wemux&lt;/a&gt; is a tool built with tmux that allows pairing through a remote server. It is very customizable and easy to use once it's set up. Though, if you don't already use tmux and a modal editor like Vim this one is a tough sell. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/zolrath/wemux" rel="noopener noreferrer"&gt;Learn more about Wemux&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Headway developers are encouraged to learn Vim and Tmux as we think they are valuable tools and we can hone our craft by sharpening out tools. When we each sharpen our tools, we own that process and empower ourselves to work smarter not harder.&lt;/p&gt;

&lt;p&gt;Headway developers have collaborated on an incredible Vim setup. Thanks Jon Kinney! It is available via open-source below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/headwayio/vim-tmux-setup" rel="noopener noreferrer"&gt;See our Vim and Tmux Setup&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvey26gpbomrkfpddsmsu.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvey26gpbomrkfpddsmsu.jpg" alt="Pairing on a computer with webcam"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Git and pull requests (PR)
&lt;/h3&gt;

&lt;p&gt;You probably already use version control of some sort. We like to leverage the tools we already have on hand. Git provides a great framework for documenting the "story" of your code and we make sure that our commit messages and branching schemes match the "story" of our software as we build. Over time this allows us to use Git to understand better how our software is built, what went on in specific pull requests, and how certain features were added. &lt;/p&gt;

&lt;p&gt;I also like to use pull requests to communicate what kind of work I'm doing. I encourage those on my team to do the same. The intent of a pull request is not to present work that is "finished" and to be added to the code base after a quick code review. The intent is to communicate the code that you are writing to the other developers to ensure that everyone agrees on the structure, libraries, and other important decisions you have made in your code. You're already going to open the PR regardless. You might as well get feedback sooner than later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests" rel="noopener noreferrer"&gt;Learn more about Git and pull requests&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Other options
&lt;/h3&gt;

&lt;p&gt;The final tool we often use for sharing terminals with other developers is Tmate. Tmate allows you to share your terminal and ssh into another developer's terminal. &lt;/p&gt;

&lt;p&gt;At Headway, we reach for tmate when we need to do something quick and dirty or just need someone's help for a few moments. When we have serious pairing sessions, we'll join a Wemux and happily code with everyone. Both Wemux and tmate are easy to set up and provide a lot of customization. They're great for sharing development work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tmate.io/" rel="noopener noreferrer"&gt;Learn more about tmate&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Leading and managing your team
&lt;/h2&gt;

&lt;p&gt;While process and approach can be powerful, people are the true heroes that bring projects to completion. Helping every person on your team grow to become better communicators and team players is vital.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We all have a responsibility to lead each other, even if that's not in our title.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The human element of project management is not easy or simple, but if you make efforts, you will see improvement. Below are some of the tools we use to gain a better understanding of each person on our team and how we can all work better together.&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%2Favsdzpomlw0uw068ydbr.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%2Favsdzpomlw0uw068ydbr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lead Honestly
&lt;/h3&gt;

&lt;p&gt;Lead Honestly is an automated tool that helps our leadership team have candid conversations about certain topics based on the person's needs. Questions are sent out once a week for our 1-1 meetings that last about fifteen minutes. These help us uncover many issues before they become a big problem. They also help each leader get to know each team member better on a personal level because they are dedicating time to that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leadhonestly.com/" rel="noopener noreferrer"&gt;Learn more about Lead Honestly&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools to understand your team
&lt;/h3&gt;

&lt;p&gt;We use a few different approaches to understanding each individual on our team and how we can work better together. From personality traits, key motivators, and how people take action. The friendly folks at &lt;a href="https://navigatethejourney.com/" rel="noopener noreferrer"&gt;Navigate the Journey&lt;/a&gt; help us manage these efforts. Below are the tools we use to do that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assessments to better understand your team&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.discprofile.com/what-is-disc/overview/" rel="noopener noreferrer"&gt;DiSC Profiles&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://motivationalcore.com/" rel="noopener noreferrer"&gt;Motivational Core: MCORE Assessment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.kolbe.com/kolbe-a-index/" rel="noopener noreferrer"&gt;Kolbe A Index Assessment&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve your retrospectives
&lt;/h3&gt;

&lt;p&gt;Taking the time to review how what went well and what didn't go well in a project helps you continuously improve your process and relationships within a team. We wrote an article all about it. If you're interested, check out the link below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://headway.io/blog/level-up-your-development-team-with-better-agile-retrospectives" rel="noopener noreferrer"&gt;Level Up Your Development Team with Better Agile Retrospectives&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Find what works best for you and your team
&lt;/h2&gt;

&lt;p&gt;The key is to take the time to evaluate the tools and methods you have and to not be afraid to try new approaches. While these tools and techniques that have worked for us, they may not be the best fit for you and your team. Our tools change as we identify issues or needs with our team or when new technologies are available. Don't be afraid to try something new. &lt;/p&gt;

&lt;h3&gt;
  
  
  Start small to experiment
&lt;/h3&gt;

&lt;p&gt;Rather than getting everyone on a new tool, try a small team first. Listen to your team and share experiences together. See what results you get. This lowers the risk in case you don't see a positive impact. It can be hard to get a whole team onboarded to new ideas or products, but that's where leadership comes in. Become an advocate for the tool and celebrate the positive results together.&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Level Up Your Development Team with Better Agile Retrospectives</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Mon, 30 Mar 2020 21:30:30 +0000</pubDate>
      <link>https://forem.com/headwayio/level-up-your-development-team-with-better-agile-retrospectives-3imd</link>
      <guid>https://forem.com/headwayio/level-up-your-development-team-with-better-agile-retrospectives-3imd</guid>
      <description>&lt;p&gt;One of the best parts about creating great software is getting to collaborate with others. It's also one of the hardest things to get right. The best software developers are not the 10x developers who know how to build everything themselves. They are the team players who know how to be a force multiplier for all the other people involved with a project.&lt;/p&gt;

&lt;p&gt;In Agile methodology, a retrospective is run at the end of each sprint. When run properly, with a willing team, a retrospective accomplishes the core mission of enhancing collaboration across a development team and increasing the overall efficacy of the development process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrospectives allow a team to identify and discuss issues within a project and work together to solve those issues.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  What is the goal of a retrospective?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Retrospectives (i.e. retros) are meetings that generally uncover three things:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What went well during the sprint?&lt;/li&gt;
&lt;li&gt;What didn't go well?&lt;/li&gt;
&lt;li&gt;What could be done better next time?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These seemingly simple questions have a &lt;em&gt;huge&lt;/em&gt; impact on team morale and cohesion and help teams refactor their process by learning from past mistakes and growing together. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Greatness is not a function of circumstance. Greatness, it turns out, is largely a matter of conscious choice, and discipline.”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jim Collins: Good to Great&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Chances are, a team is a mix of different skill levels, aptitudes for social interaction, and hopefully different backgrounds. It is said that diverse teams build better software, but how does a team learn to gel? How do people know the right feedback to give or when to give it? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A project or week doesn't go well?&lt;/li&gt;
&lt;li&gt;Rifts begin to build in teams?&lt;/li&gt;
&lt;li&gt;People get angry or frustrated and have unproductive disagreements?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It would seem like stopping everything to address those concerns would make sense - however, the feature requests and bug reports don't stop, and the team still needs to deliver effective software. If left unaddressed, those ragged edges will continue to fray and eventually pull teams apart. So what's the solution? While not a silver bullet, sprint retrospectives are a great place to start.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c6QBDKsX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/il1wy58ibzaogddsv9on.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c6QBDKsX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/il1wy58ibzaogddsv9on.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Make retrospective meetings more productive
&lt;/h2&gt;

&lt;p&gt;Meetings can often have too much unhealthy agreement vs. healthy disagreement, with retros commonly falling into the former. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what are the pieces that need to come together for an effective retro?&lt;/strong&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Set ground rules
&lt;/h3&gt;

&lt;p&gt;Instead of jumping right to the formal retrospective questions above (which can often lead to blank looks or tacit agreement), start by laying the ground rules. This frames the conversation and sets up people to think about the conflict before it starts. &lt;/p&gt;

&lt;p&gt;Here are some ground rules that are helpful to establish:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be respectful&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating software and building products is much more than just putting in time and money, and having excellent features come out. Everyone is on the same team - work together, respect one another, leave egos at the door, and enter each retro with a growth mindset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's said in a retro stays in the retro&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Retros are the time and place to push people to be their best selves, be critical, and talk about how certain behaviors impacted the team. It's a safe space to ask for help and identify areas of improvement. It's also best practice to not keep audio/video recordings, and clarify that notes of the meeting will be shared between the meeting attendees only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal is to work on the process&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When developers write software, it's a reasonably personal endeavor - and sometimes they feel other people are blocking their work. But it's important to realize everyone is on the same team, and it's not the time or place to make things personal. Questions about code quality are not the point of a retro and should stay in respectful code reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stay on track
&lt;/h3&gt;

&lt;p&gt;In general, retros should be around an hour. Software developers are well known for "solutionizing" and over-engineering. But the goal of a retro should be to suss out issues - not come up with solutions to every problem. It's easy to break down the first problem that is presented and spend the rest of the meeting coming up with a solution. Instead, note the issues and move on. Problems that require follow up will become apparent, and solutions for those issues should happen in a separate meeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus on unity
&lt;/h3&gt;

&lt;p&gt;It should go without saying that projects are more effective when team members feel safe to share their experiences and have a desire to work on growing together. Retros are not a place for anger or decision making by seniority. All ideas should be on the table, and nothing should be shot down without healthy discussion. Effective retros will elevate the entire sprint process by helping the team to work with, and not against, each other.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If you could get all the people in the organization rowing in the same direction, you could dominate any industry, in any market, against any competition, at any time."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Patrick Lencioni&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Have a prepared facilitator
&lt;/h3&gt;

&lt;p&gt;The facilitator should guide the meeting intentionally, not letting any one person or topic dominate the discussion in an unhealthy way. This process can often be difficult when team members are invested in their issue or point of view, resulting in lots of discussion on how to move forward. The goal here is to make sure everyone is heard and divert conversations that aren't immediately applicable to future actionable items.&lt;/p&gt;

&lt;p&gt;The bottom line - if the facilitator senses unnecessary discussion, off-track topics, or that things are going too far, they need to get the meeting back on track.&lt;/p&gt;

&lt;h3&gt;
  
  
  Have actionable takeaways
&lt;/h3&gt;

&lt;p&gt;It's not enough to write items down and never revisit them. Each action item should either be assigned to an individual or put in place as a new standard for the team. That way rather than only reminiscing on the tough parts of the last sprint, having clear action items and assigning them to the right people will leave everyone feeling empowered, excited, and with a sense of forward momentum. Write action items down during the retro and review them at the end to get buy-in and a sense of shared team accountability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJ7PLVE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e7cxmz8qmavyekfr7iy7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJ7PLVE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e7cxmz8qmavyekfr7iy7.jpg" alt="Discussion about ordering"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  When retrospectives don't go well
&lt;/h1&gt;

&lt;p&gt;That all sounds great...but in reality, it's not always that cut and dried. What are some common reasons retros don't go as planned? &lt;/p&gt;

&lt;h3&gt;
  
  
  Retros are an afterthought
&lt;/h3&gt;

&lt;p&gt;This is the most common reason given from people who don't like retros. When they seem like an afterthought and little time is spent on them, of course, they aren't well received. But, just like many things in agile, the important part of sprinting is taking action to correct issues over time - not letting those issues continue sprint after sprint. &lt;/p&gt;

&lt;h3&gt;
  
  
  There are no effective outcomes to the retro
&lt;/h3&gt;

&lt;p&gt;Retros provide a format for everyone to be heard on a development team. Sometimes being heard is enough, and retros can provide a safe space to vent. But some issues are more critical than others and require action to be taken - sometimes immediately. If those issues aren't taken seriously and no corrective measures are put in place, it can feel like a lot of wasted effort. Keeping good notes and writing down action items is a good first step to combat this issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  There are zero or few issues brought up from the retro
&lt;/h3&gt;

&lt;p&gt;There are always problems - no matter how many people tell you that everything is peachy, things can always get better. For teams that haven't seen a retro before, or have seen ineffective retros, there can often be a propensity to just ignore issues because "nothing changes".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ESeOkWGU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/g9j38z5mnw8zo0wrjq6c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ESeOkWGU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/g9j38z5mnw8zo0wrjq6c.jpg" alt="Group discussion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Refresh your retros with new ideas
&lt;/h1&gt;

&lt;p&gt;The solutions to these problems are not to stop doing retros. It's rare to hear, "the process is perfect, we should never change it" in any development project. So what are some ways to approach retros in new ways and unlock a better process?&lt;/p&gt;

&lt;h3&gt;
  
  
  Start with a light question
&lt;/h3&gt;

&lt;p&gt;Developers are often stuck working in their own world. They often have to stretch their comfort zones to work well with others - but it can be hard to connect with everyone, and some people on the team may be less outspoken. One way to help set the tone is to ask an ice breaker question or a question focused on feeling. People open up more and are better able to give/receive criticism when they feel they are in a safe and trusted environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some suggestions for kicking off the meeting:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask for a "feel color" (i.e., red if things are going terribly, green if things are going great)&lt;/li&gt;
&lt;li&gt;Ask about weekend plans&lt;/li&gt;
&lt;li&gt;Other ice breaker topics that can get the room talking&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Change the facilitator
&lt;/h3&gt;

&lt;p&gt;In the capital "A" Agile methodology, retros are supposed to be run by a scrum master. However, this choice can result in lower than ideal participation - especially if no action occurs. This meeting should not be hogged by a team lead or someone "up above" - this is to help developers increase efficiency. One way to encourage the team to be open and have a growth mindset is to have one of the developers to facilitate the meeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask for feedback before the retro
&lt;/h3&gt;

&lt;p&gt;If there is a group of non-participants, it can be helpful to ask those people to write their opinions down before the retro. Then at the meeting, ask those people to share what they wrote down prior. This goes a long way to increasing participation from people who feel they can't share at the moment or need more time for preparation.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Retros at Headway
&lt;/h2&gt;

&lt;p&gt;Retrospectives are crucial for us here at Headway. They not only help us hone our process and continuously improve each week as we work to bring new ideas to market - they also help improve relationships with clients and partners, and in turn, help us build better products.&lt;/p&gt;

&lt;p&gt;Retros fit well with our &lt;a href="https://headway.io/about/"&gt;Guiding Manifesto&lt;/a&gt; and are imperative to ensure that we grow together, rather than separately. While we still spend time individually improving ourselves and enhancing our craft, retros let us do that as a team - which improves our ability to &lt;strong&gt;deliver software together&lt;/strong&gt; on a sprint by sprint basis.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Free Download - Project retrospective template
&lt;/h2&gt;

&lt;p&gt;This is the retro document we use at Headway. We hope you'll give it a shot! If you find it useful, feel free to share it with your friends and colleagues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.notion.so/hdwy/Retrospective-Template-db6d7835c78746d680b3089dfd515c98"&gt;Template for Retrospective Meetings&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Amy Marco for encouraging me to explore team building and for the structure of the awesome retro document that I continue to use to this day.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agile</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Regain control of branches with git rebase --onto</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Wed, 15 May 2019 12:00:00 +0000</pubDate>
      <link>https://forem.com/headwayio/regain-control-of-branches-with-git-rebase-onto-3075</link>
      <guid>https://forem.com/headwayio/regain-control-of-branches-with-git-rebase-onto-3075</guid>
      <description>&lt;p&gt;&lt;em&gt;This article originally appeared on &lt;a href="https://headway.io/blog/regain-control-of-branches-with-git-rebase-onto/" rel="noopener noreferrer"&gt;Headway's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rebasing branches with &lt;code&gt;git&lt;/code&gt; is a process that all developers work through. It can be challenging at times to get all your commits exactly where you're hoping that they will end up. While &lt;code&gt;git&lt;/code&gt; isn't perfect, &lt;code&gt;git rebase --onto&lt;/code&gt; can help tame your branches when you have to work with non standard rebasing. &lt;code&gt;git rebase --onto&lt;/code&gt; let's us be a little more choosy about where and which commits we want to more easily handle complicated rebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a traditional rebase works
&lt;/h2&gt;

&lt;p&gt;Traditional rebases (those without the &lt;code&gt;--onto&lt;/code&gt; flag) are pretty easy to understand. Commits get applied to the head of the branch you originally created your working brach off of this works for the majority of cases but sometimes the head of your base branch is faulty, or your doing some testing and want to move your feature branch one commit at a time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjjus9otbsf7m5zp54y8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjjus9otbsf7m5zp54y8.png" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the following examples are pretty contrived, but I think &lt;code&gt;git rebase --onto&lt;/code&gt; is applicable to a number of situations. Testing large feature branches, or moving a feature branch from one base to another base. Reasons to use &lt;code&gt;git rebase --onto&lt;/code&gt; include fixing mistakes, or moving a feature off of a feature branch and onto a base branch of some kind. &lt;code&gt;git rebase --onto&lt;/code&gt; can be your friend when you need a scalpel for your &lt;code&gt;git rebase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To easily reproduce a bunch of commits in a repo and make it easy to work with, here is a quick bash script that will generate a ruby file and commit a bunch of times to make our "git world" more consistent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  #!/bin/bash

  function create_basic_git_repo()
  {
    echo 'git rebase is great'
    mkdir gitexample
    cd gitexample
    git init
    mkdir lib
    touch lib/my_thing.rb
    echo "class MyThing; def my_method; 1 + 1 == 2; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'Commit 1'
    echo "class MyThing; def my_method; 1 + 2 == 3; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'Commit 2'
    echo "class MyThing; def my_method; 1 + 3 == 4; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'Commit 3'
    git checkout -B My_Feature_Branch
    echo "class MyThing; def my_method; 1 + 4 == 5; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'My Feature Commit 1'
    echo "class MyThing; def my_method; 1 + 5 == 6; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'My Feature Commit 2'
    git checkout master
    echo "class MyThing; def my_method; 1 + 6 == 7; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'Commit 4'
    echo "class MyThing; def my_method; 1 + 7 == 8; end; end" | tee lib/my_thing.rb
    git add lib/my_thing.rb
    git commit -m 'Commit 5'
  }

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

&lt;/div&gt;



&lt;p&gt;In this repo you should see a &lt;code&gt;lib&lt;/code&gt; directory as well as a file called &lt;code&gt;my_thing.rb&lt;/code&gt; with some commits on. 5 commits on the &lt;code&gt;master&lt;/code&gt; branch and 2 commits on a feature branch that are based off of the third commit of the &lt;code&gt;master&lt;/code&gt; branch. The SHA's between your repo and my repo will be different, but at least the commit messages will be the same to easily follow what we are doing below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Object Identifiers
&lt;/h2&gt;

&lt;p&gt;As a quick aside, let's talk about object identifiers in git for a second.&lt;/p&gt;

&lt;p&gt;An object identifier usually identifies a &lt;code&gt;commit&lt;/code&gt;, we often call them &lt;code&gt;SHA's&lt;/code&gt; but we can also be talking about a &lt;code&gt;branch.&lt;/code&gt; When we refer to set a of work by the &lt;code&gt;branch&lt;/code&gt; name we are just talking about the collection of &lt;code&gt;commit&lt;/code&gt;s that make up and we reference the head of that collection of &lt;code&gt;commit&lt;/code&gt;s by the name of the branch.&lt;/p&gt;

&lt;p&gt;Any object identifier is a valid argument with a &lt;code&gt;rebase&lt;/code&gt;. We can rebase on a branch name, a SHA, a tag, as long as it identifies a commit, it is a valid argument to &lt;code&gt;rebase&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git rebase —onto
&lt;/h2&gt;

&lt;p&gt;There are two versions of &lt;code&gt;git rebase --onto&lt;/code&gt; , the binary and ternary functions. Also written as &lt;code&gt;git rebase --onto/2&lt;/code&gt; and &lt;code&gt;git rebase --onto/3&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git rebase —onto/2
&lt;/h2&gt;

&lt;p&gt;The two argument version of git rebase onto is used to move a set of commits from one one object identifier to any arbitrary object identifier.&lt;/p&gt;

&lt;p&gt;In words I want to move my set of commits from one commit to any other arbitrary commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapiiq5uiba51ir36dxyi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapiiq5uiba51ir36dxyi.png" width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the most part, this is helpful if you don't want to move your commits to the top of a branch or for some reason you can't have your feature branch at the top of &lt;code&gt;master&lt;/code&gt; . But it can also be useful if you have a release branch and need to maintain your work across multiple branches.&lt;/p&gt;

&lt;p&gt;I like to think about &lt;code&gt;git rebase --onto/2&lt;/code&gt; git rebase "where I want to go" from "where I was", with "where I want to go" being the object identifier of the place you want all your commits to go to and "where I was" to be the object identifier of where your branch is currently sitting off at.&lt;/p&gt;

&lt;p&gt;For example in this case I did &lt;code&gt;git rebase --onto 2b4c572 b45626d&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I want my work to be on top of commit 4 with SHA &lt;code&gt;2b4c572&lt;/code&gt; and it's currently on commit 3 with SHA &lt;code&gt;b45626d&lt;/code&gt;. Not too bad or crazy complicated. For these cases I took the changes from &lt;code&gt;My Feature Commit 1&lt;/code&gt; and &lt;code&gt;My Feature Commit 2&lt;/code&gt; to make sure the commits applied to see the example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froy0tnpnjoq2beivvrjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froy0tnpnjoq2beivvrjn.png" width="636" height="268"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bcy6w6x1lilt35twp5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bcy6w6x1lilt35twp5f.png" width="710" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Git rebase --onto/3
&lt;/h2&gt;

&lt;p&gt;The three argument version of git rebase --onto is a little more powerful, but not as useful always. I tend to only use it when I have really messed something up or have a pretty nifty reason to use it. In fact, I am not certain I have ever used it to do something entirely &lt;em&gt;useful&lt;/em&gt; in git repository. Either way, it could be useful at some point.&lt;/p&gt;

&lt;p&gt;For this one, we want to move my set of commits from one object identifier to another object identifier, but I want to only select a certain amount of commits from my branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18mcbq1szunz3kip4e4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18mcbq1szunz3kip4e4x.png" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that we masterfully and easily cut out commit G from our feature branch. This makes it pretty easy to do this.&lt;/p&gt;

&lt;p&gt;We want to &lt;code&gt;git rebase --onto "where I want to go" from "where we were" up to "my chosen commit"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example here we could do &lt;code&gt;git rebase --onto c5d6535 42fa4e5 22d71a4&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We are at commit 42fa4e5 and we want just commit 22d71a4 on top of commit c5d6536.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falp91f7fmidf0ergmwcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falp91f7fmidf0ergmwcl.png" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05qfdswkz5hq1jcie3vz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05qfdswkz5hq1jcie3vz.png" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty easy if you ask me. One thing to note is that &lt;code&gt;git rebase --onto/3&lt;/code&gt; leaves &lt;code&gt;HEAD&lt;/code&gt; in a detached state and to save this state, you need to name the branch something.&lt;/p&gt;

&lt;p&gt;All of these examples were generated using a quick bash script that generates the same commits each time. Though the commit SHA's (object identifiers) will be different (thanks Git!).&lt;/p&gt;

&lt;p&gt;Feel free to check out the bash script and play around with &lt;code&gt;git commit rebase --onto&lt;/code&gt; it can take some getting used to but its a powerful tool that has really helped me minimize some &lt;code&gt;git&lt;/code&gt; pain in the past.&lt;/p&gt;

&lt;p&gt;Have any questions? Let me know below. &lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more about Git rebasing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-rebase" rel="noopener noreferrer"&gt;Git Rebase Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://content.pivotal.io/blog/git-rebase-onto" rel="noopener noreferrer"&gt;Git Rebase —onto Tutorial&lt;/a&gt;&lt;/p&gt;

</description>
      <category>development</category>
      <category>git</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Build a Simple Static Site on Amazon S3</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Wed, 13 Feb 2019 12:00:00 +0000</pubDate>
      <link>https://forem.com/headwayio/build-a-simple-static-site-on-amazon-s3-1nld</link>
      <guid>https://forem.com/headwayio/build-a-simple-static-site-on-amazon-s3-1nld</guid>
      <description>&lt;p&gt;&lt;em&gt;This article originally appeared on &lt;a href="https://http://headway.io/blog/deploy-static-s3/" rel="noopener noreferrer"&gt;Headway's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today I am going to walk you step by step on how to build a simple static website using &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt;. Sometimes all you need is a little HTML and CSS for a successful website. In fact, it can often be faster to develop a website without an entire web framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a static site?
&lt;/h2&gt;

&lt;p&gt;A static site is a website that is a collection of HTML and other assets that does not get served from a web framework. A static site is in no way non-interactive though.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;static&lt;/em&gt; part just refers to the way the HTML and other assets are served to browsers when a user navigates to your website. There is no web framework &lt;em&gt;serving&lt;/em&gt; those assets to the browser dynamically, the site loads up and then uses JavaScript to interact with other web servers and API's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples of cool web applications that are static:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mint.com/" rel="noopener noreferrer"&gt;Mint: Budget Tracker &amp;amp; Planner&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.dropbox.com" rel="noopener noreferrer"&gt;Dropbox&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the value of using a static site?
&lt;/h2&gt;

&lt;p&gt;HTML and CSS are all you need to get started with a simple static site.&lt;/p&gt;

&lt;p&gt;Static sites do not need to be served by a web server. You don't need the infrastructure that is otherwise required to host a web framework. Developing a website without an entire web framework can be more efficient.&lt;/p&gt;

&lt;p&gt;You also benefit from a separation of concerns, your front end is just that, no templating languages or calling methods on your models in your view. You get to just deal with the frontend without worrying about the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The steps for building your first static site:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Build a tiny bit of HTML&lt;/li&gt;
&lt;li&gt;Upload the HTML to an AWS S3 Bucket&lt;/li&gt;
&lt;li&gt;Make the Bucket accessible and public&lt;/li&gt;
&lt;li&gt;Add some JS and CSS to make your "static" site shine!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting started with your HTML file
&lt;/h2&gt;

&lt;p&gt;The easiest way to do it, is to create an HTML file. Traditionally this is called index.html but the naming doesn’t matter too much. You can point the web server to serve whatever HTML file you want.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    Hello World!
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now you have some basic HTML and you need to serve that somewhere. One of the easiest options is to simply upload the HTML file to an S3 bucket on AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new bucket on AWS
&lt;/h2&gt;

&lt;p&gt;First, head to the S3 console on AWS. You need to add a new bucket.&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%2Fuploads%2Farticles%2Fbsl87sego4ck6jp81w18.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%2Fbsl87sego4ck6jp81w18.png" alt="Add a new bucket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this example, I just created a public bucket named "testheadway." Don’t worry about the settings yet. Just click through to get the bucket created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit the bucket for hosting abilities
&lt;/h2&gt;

&lt;p&gt;Next you need to turn on the ability for the bucket to host a static website. Click on the bucket to edit it.&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%2Fuploads%2Farticles%2F5plq1fhm802tku0pbk1j.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%2F5plq1fhm802tku0pbk1j.png" alt="Edit S3 bucket"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we’re in the edit view for the bucket, head to the "Properties" tab for the bucket.&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%2Fuploads%2Farticles%2Fnmvoh9gqtxmr9fhayul8.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%2Fnmvoh9gqtxmr9fhayul8.png" alt="Click on properties"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click into the Static website hosting card.&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%2Fuploads%2Farticles%2F83yau4vl63dj8zukd7mr.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%2F83yau4vl63dj8zukd7mr.png" alt="Click on static website hosting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now select "Use this bucket to host a website" and add the name of your index document this is almost always going to be your index.html it should match the name of the document you created earlier.&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%2Fuploads%2Farticles%2F3i8vhjjceu55ij3cop7o.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%2F3i8vhjjceu55ij3cop7o.png" alt="Static website radio button and index file name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Easy peasy if you ask me!&lt;/p&gt;

&lt;p&gt;Now, once this is turned on you should see an endpoint for your S3 bucket. This is the URL used to access your newly created static website.&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%2Fuploads%2Farticles%2F4q62kpsepo1rf49b1uix.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%2F4q62kpsepo1rf49b1uix.png" alt="Url for static site"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit the bucket permissions
&lt;/h2&gt;

&lt;p&gt;You need to adjust a few more settings to have public access to this S3 bucket. The bucket permissions need to allow everyone to getObject's from your bucket. As basic policy could look like the below. Just make sure to update the "Resource" to be the name of your S3 bucket.&lt;/p&gt;

&lt;p&gt;Note: This is not secure nor recommended for production ready static sites. Seek an AWS security expert for recommended bucket permissions.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::yourbucketname/*"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Copy and paste the below in the Bucket Policy by heading to Permissions.&lt;/p&gt;

&lt;p&gt;The next step is to click into the Bucket Policy.&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%2Fuploads%2Farticles%2Fznjnpq9b2uf2juohvtll.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%2Fznjnpq9b2uf2juohvtll.png" alt="Edit bucket policy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you need to save the getObject policy. AWS will warn you that you are giving public access to your objects in your bucket. This policy only allows people to access objects in your bucket, which is what you want since people are going to be accessing your index.html.&lt;/p&gt;

&lt;p&gt;If you head to that URL now you can see that you get a giant 404.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is expected, you haven’t uploaded your index.html file yet.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wp97wdufqlwgnbijrq5.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%2F0wp97wdufqlwgnbijrq5.png" alt="First time is a 404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Upload the index.html
&lt;/h2&gt;

&lt;p&gt;The next step is to upload your index.html.&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%2Fuploads%2Farticles%2Flwfvypehyuhkhawjn5bt.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%2Flwfvypehyuhkhawjn5bt.png" alt="Upload html file to s3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, if you head back to the bucket URL you should see your HTML rendered in the browser.&lt;/p&gt;

&lt;p&gt;Congratulations, you’ve got a basic website being hosted from AWS!&lt;/p&gt;

&lt;p&gt;Here are some ways you can extend the site easily with some CSS and JS.&lt;/p&gt;

&lt;p&gt;In the same way that it was pretty easy to serve straight up HTML, it’s just as easy to add some CSS to your application.&lt;/p&gt;

&lt;p&gt;Stick a new file into your directory called styles.css. Like other static site generators you can add some basic styling to your HTML just by referencing the CSS in your HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your HTML should now look like:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"&amp;gt;
    &amp;lt;link rel="stylesheet" href="styles.css"&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class='center'&amp;gt;
      &amp;lt;h1&amp;gt;
        Hello World!
      &amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Now you can add some styling to the styles.css style sheet like:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 {
  font-family: 'Roboto', sans-serif;
}
.center {
  width: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now you just need to delete the index.html file from the S3 bucket and upload these two files and you should have a minimally styled website up and running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It should look something like:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm9ph4bik78uoeda75b8.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%2Fpm9ph4bik78uoeda75b8.png" alt="Static site with minimal styling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using jQuery to add new elements
&lt;/h2&gt;

&lt;p&gt;So far you have done all of this with no Javascript and no web server or application server. How can you interact with the world outside of your little app? Well, you need to add some JavaScript to make that happen. But, that is pretty easy too. Just update the HTML to pull in some JavaScript, you are going to use jQuery to quickly grab some weather and display it on your static site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You only have three files. Pretty easy and simple, but very powerful.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link href="[https://fonts.googleapis.com/css?family=Roboto](https://fonts.googleapis.com/css?family=Roboto)" rel="stylesheet"&amp;gt;
    &amp;lt;link rel="stylesheet" href="styles.css"&amp;gt;
    &amp;lt;script src="[http://code.jquery.com/jquery-latest.min.js](http://code.jquery.com/jquery-latest.min.js)" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="weather.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class='center'&amp;gt;
      &amp;lt;h1&amp;gt;
        Hello World!
      &amp;lt;/h1&amp;gt;
      &amp;lt;button id='weather_button' class='basic_button'&amp;gt;
        Get my Weather
      &amp;lt;/button&amp;gt;
      &amp;lt;div class='error'&amp;gt;
      &amp;lt;/div&amp;gt;

      &amp;lt;div class='weather'&amp;gt;
          &amp;lt;div class='city-name text'&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class='weather-main text'&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class='description text'&amp;gt;
          &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Your slightly more complete CSS:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 {
  font-family: 'Roboto', sans-serif;
}

.center {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.text {
  margin-top: 30px;
  font-family: 'Roboto', sans-serif;
}

.basic_button {
  font-family: 'Roboto', sans-serif;
  font-size: 18;
  margin-top: 30px;
  width: 300px;
  height: 45px;
  border-radius: 4px;
  background-color: #44AAB5
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Some JS to interact with a weather API and make your static site a little more interesting.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$(document).ready(function() {
  $("#weather_button").click(function(e) {
    console.log(e)
    e.preventDefault();
    $.ajax({
      type: "POST",
      data: 'json',
      url: 'https://api.openweathermap.org/data/2.5/weather?zip=98229,us&amp;amp;appid=useyourownopenweathermapkeyplease',
      success: function(result) {
        console.log(result)
        $( ".city-name" ).text(result.name);
        $(".weather-main").text(
          result.weather &amp;amp;&amp;amp;
          result.weather[0] &amp;amp;&amp;amp;
          result.weather[0].main
        )
        $(".description").text(
          result.weather &amp;amp;&amp;amp;
          result.weather[0] &amp;amp;&amp;amp;
          result.weather[0].description
        )
      },
      error: function(result) {
        console.log('error', result)
        $(".error").text(result.error)
      }
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Checkout the final product here, it’s not so static after all 😉:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://testheadway.s3-website-us-west-2.amazonaws.com/" rel="noopener noreferrer"&gt;See the "testheadway" website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This same process stands with a single page application (SPA) with React. If you want to host your SPA on S3 all you would need to do is build your React app and then add all the static assets (HTML, JS, CSS files) to your S3 bucket.&lt;/p&gt;

&lt;p&gt;If you built your SPA from a &lt;code&gt;create-react-app&lt;/code&gt; application there is already a build command to build your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need to run:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This should build your static assets and compile them into the build directory for your application.&lt;/p&gt;

&lt;p&gt;Instead of uploading a single HTML file, you'll upload the contents of the build directory to the S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;my-react-directory/build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now your React app will mount when it finds your index.html.&lt;/p&gt;

&lt;h2&gt;
  
  
  The advantages of building a static site vs a dynamic site
&lt;/h2&gt;

&lt;p&gt;There are many ways to build web applications in todays day in age, and one of them is to host a static site and let it interact with your backend.&lt;/p&gt;

&lt;p&gt;While there are certainly challenges to building websites in this manner there are plenty of positives as well, including separating concerns between the front end and the backend, more easily supporting multiple clients, and better supporting modern JavaScript SPA’s.&lt;/p&gt;

&lt;p&gt;Overall this is a quick overview of how you can build a sample application completely from a static website that is easily and cheaply host on Amazon S3.&lt;/p&gt;

&lt;p&gt;Let me know if you have any questions about static sites or if we can help you build a SPA, a static site, or help you deploy your static site to Amazon S3, we’re happy to help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended resources for building static sites
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deploying a create React app&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://facebook.github.io/create-react-app/docs/deployment" rel="noopener noreferrer"&gt;Facebook Github - Create React App: Deployment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static site generators:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;GatsbyJS&lt;/a&gt;&lt;br&gt;
&lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Places to host static websites:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>development</category>
      <category>s3</category>
      <category>staticsites</category>
      <category>react</category>
    </item>
    <item>
      <title>I love git log</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Mon, 20 Aug 2018 18:35:40 +0000</pubDate>
      <link>https://forem.com/drews256/i-love-git-log-off</link>
      <guid>https://forem.com/drews256/i-love-git-log-off</guid>
      <description>&lt;h4&gt;
  
  
  The title of this post should be IFLG (like IFLS, but ya know with Git).
&lt;/h4&gt;

&lt;p&gt;But it's probably not great to title a post with the f*** word. Ya know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git is the best
&lt;/h2&gt;

&lt;p&gt;Really, I love git. I think it is ridiculously useful and when used properly, it can speed up development on a team tremendously. When I was first learning git though, I always got so frustrated by not being able to &lt;em&gt;see&lt;/em&gt; what was going on with git, like what were my teammates doing and how did my work fit in with what they were doing?&lt;/p&gt;

&lt;p&gt;I tried a few UI's for working with git but was never satisfied, everything seemed clunky and you had to get out of the terminal to go look at the UI and then come back and figure out what you were doing again. &lt;/p&gt;

&lt;p&gt;One of my favorite commands when I am working with git repo's is (hopefully unsurprisingly) &lt;code&gt;git log&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This shows a log of all of your commits and the basic command outputs something along the lines of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 8c2ca7c38f50e1d837485edd946916bfdciii20a1 (HEAD -&amp;gt; master, origin/master)
Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
Date:   Mon Aug 20 10:20:27 2018 -0700

    Fixes the resetting of form fields on the settings form

commit cc309a7cb45bfcfd7e94a49387086266dff54c05
Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
Date:   Mon Aug 20 10:03:46 2018 -0700

    Updates some styling issues and copy

commit 2d848cec12038475b9f12070547653ab212e4e6
Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
Date:   Mon Aug 20 08:17:13 2018 -0700

    Add config and format correctly

commit deec64859bc9b2ef74a987437b7864f2183940e
Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
Date:   Thu Aug 16 11:11:42 2018 -0700

    First commit, I never know what to put here?

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



&lt;p&gt;So what do we have going on here?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 8c2ca7c38f50e1d837485edd946916bfdciii20a1 (HEAD -&amp;gt; master, origin/master)
Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
Date:   Mon Aug 20 10:20:27 2018 -0700

    Fixes the resetting of form fields on the settings form
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Overall, it's pretty easy to see the ordering of the commits, top to bottom, first commit to last commit. You get the revision number which uniquely identifies the individual revision. Next to that, you see some more information about your branch in the parentheses, these reference your &lt;code&gt;HEAD&lt;/code&gt; (which is what you have checked out now). You also &lt;code&gt;origin/master&lt;/code&gt; which means that &lt;code&gt;origin/master&lt;/code&gt; is at this commit as well. So your &lt;code&gt;HEAD&lt;/code&gt; and &lt;code&gt;origin/master&lt;/code&gt; are at the same revision. Then you get an Author and a Date. Finally, you get the commit message. So far so good. Nothing too complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diving into the &lt;code&gt;git log&lt;/code&gt; command
&lt;/h2&gt;

&lt;p&gt;If you do a &lt;code&gt;git log --help&lt;/code&gt; you get ample output, in fact one reason I love git is the massive amount of documentation that allow you to really dive into and customize what you want. I'm gonna run through a few of my favorite options that let you really see what is going on in your repository.&lt;/p&gt;

&lt;p&gt;The first is &lt;code&gt;git log --graph&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Which lets you see a graph of the branches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* commit 8c2ca7c38f50e1d837485edd946916bfdciii20a1 (HEAD -&amp;gt; master, origin/master)
| Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
| Date:   Mon Aug 20 10:20:27 2018 -0700
|
|     Fix the resetting of form fields on the settings form
|
* commit cc309a7cb45bfcfd7e94a49387086266dff54c05
| Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
| Date:   Mon Aug 20 10:03:46 2018 -0700
|
|     Update some styling issues and copy
|
*   commit a9b263cacd3240f6d00e7ce401fc6f6c23988cee
|\  Merge: 2d848cec deec4187
| | Author: Andrew Stuntz &amp;lt;andrewbstuntz@gmail.com&amp;gt;
| | Date:   Mon Aug 20 08:19:44 2018 -0700
| |
| |     Merge branch 'master' of some/private/repo
| |
| *   commit deec398745bc9b2ef74a418287b7864f2183940e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Already we are starting to get a little more visual, you see the commit easily from one branch into the master branch. You can also see at the bottom the commit that was made onto that branch before it was merged into the master branch.&lt;/p&gt;

&lt;p&gt;The next command I like a lot is &lt;code&gt;git log --oneline&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This takes the output and makes each commit a single line, so it vastly increases the amount of commits that you can see at any give time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8c2ca7c3 (HEAD -&amp;gt; master, origin/master) Fix the resetting of form fields on the settings form
cc309a7c Update some styling issues and copy
a9b263ca Merge branch 'master' of https://github.com/drews256/some/private/repo
2d848cec Add config and format correctly
deec4187 Merge pull request #1303940 from drews256/some-other-branch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have the same amount of information, but succinctly displayed in 5 lines. &lt;/p&gt;

&lt;p&gt;You can even combine the two and get &lt;code&gt;git log --oneline --graph&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* 8c2ca7c3 (HEAD -&amp;gt; master, origin/master) Fix the resetting of form fields on the settings form
* cc309a7c Update some styling issues and copy
*   a9b263ca Merge branch 'master' of https://github.com/drews256/some/private/repo
|\
| *   deec4187 Merge pull request #1303940 from drews256/some-other-branch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You get the best of both worlds, you see the graph and branching plus the commits and now its displayed succinctly.&lt;/p&gt;

&lt;p&gt;If you have &lt;code&gt;color&lt;/code&gt; globally turned on for your git repos, this will display in full color, with each branch having a unique color which makes it very easy to follow. If that isn't the case and you want to add some color to your output, you can run &lt;code&gt;git log --oneline --graph --color&lt;/code&gt; and you will see each branch nicely colored for you to explore.&lt;/p&gt;

&lt;p&gt;Now you can see what and where your branches are easily and make sure your not working in or on places you shouldn't be.&lt;/p&gt;

&lt;p&gt;What are your favorite parts of git?&lt;/p&gt;

&lt;p&gt;Thanks &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow"&gt;atlassian&lt;/a&gt; for the cover photo.&lt;/p&gt;

</description>
      <category>git</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Ridiculously easy row and column layouts with Flexbox</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Fri, 10 Aug 2018 20:29:05 +0000</pubDate>
      <link>https://forem.com/drews256/ridiculously-easy-row-and-column-layouts-with-flexbox-1k01</link>
      <guid>https://forem.com/drews256/ridiculously-easy-row-and-column-layouts-with-flexbox-1k01</guid>
      <description>&lt;h1&gt;
  
  
  Super easy responsive Row and Columns in straight up CSS
&lt;/h1&gt;

&lt;p&gt;Grid layouts are the bread and butter of web development design and chances are you've reached for something like Bootstrap or Foundation to make your layouts a reality. But, like most of you, I don't have a fondness of the dependencies required to run Bootstrap or Foundation nor do I like the ding to my page load times.&lt;/p&gt;

&lt;p&gt;In fact when I use Bootstrap for an application that I am writing, I really almost only ever use it for the grid layouts, sometimes I will use it for notifications or basic fairly sensible CSS defaults, but 90% of the time, all I want is the grid layouts. &lt;/p&gt;

&lt;p&gt;I also don't appreciate only have options of splitting columns into 12 columns or less. It feels like you sometimes have to do some crazy work arounds to get columns in columns, or strange things like that. &lt;/p&gt;

&lt;p&gt;How can we do grid layouts faster and easier? Flexbox is your answer. I think &lt;/p&gt;

&lt;h3&gt;
  
  
  Flexbox
&lt;/h3&gt;

&lt;p&gt;At this point flexbox is pretty much everywhere. It's supported by all major &lt;a href="https://caniuse.com/#feat=flexbox" rel="noopener noreferrer"&gt;browsers&lt;/a&gt;. It allows for much easier layouts and is supported by React-Native which means when I layout pages for React-Native I reach for flexbox first, but I have found my self reaching for flexbox first in web development as well.&lt;/p&gt;

&lt;p&gt;In fact the last application I laid out, I did entirely with flexbox. I have found it &lt;em&gt;that&lt;/em&gt; easy to use.&lt;/p&gt;

&lt;p&gt;If you don't know too much about flex box. I like this page that gives a good run down of &lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/" rel="noopener noreferrer"&gt;flexbox&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout
&lt;/h3&gt;

&lt;p&gt;First I wrap the entire page in a div.&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'some-page-wrapper'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I define a &lt;code&gt;.row&lt;/code&gt; and &lt;code&gt;.column&lt;/code&gt; class that can help with the layout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&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="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&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="nc"&gt;.column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-basis&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;flex&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we want a two column layout:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'some-page-wrapper'&lt;/span&gt;&lt;span class="nt"&gt;&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;'row'&lt;/span&gt;&lt;span class="nt"&gt;&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;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'blue-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column One
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'green-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column Two
      &lt;span class="nt"&gt;&amp;lt;/div&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&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSS looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.some-page-wrapper&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;15px&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;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&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="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&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="nc"&gt;.column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-basis&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;flex&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="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.blue-column&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;blue&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;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.green-column&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;green&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;100px&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;&lt;iframe height="600" src="https://codepen.io/drews256/embed/zLerNx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;What if we wanna add a third column? The HTML is easily updated to:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'some-page-wrapper'&lt;/span&gt;&lt;span class="nt"&gt;&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;'row'&lt;/span&gt;&lt;span class="nt"&gt;&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;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'blue-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column One
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'green-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column Two
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'orange-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column Two
      &lt;span class="nt"&gt;&amp;lt;/div&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&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/drews256/embed/djaYKo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And we get a third column added. That seamlessly nests itself in our row. &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/drews256/embed/djaYKo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Now what if we want more complicated layouts? &lt;/p&gt;

&lt;p&gt;We can just add more rows, that is pretty easy.&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'some-page-wrapper'&lt;/span&gt;&lt;span class="nt"&gt;&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;'row'&lt;/span&gt;&lt;span class="nt"&gt;&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;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'orange-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column One
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'blue-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column Two
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'green-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Column Three
      &lt;span class="nt"&gt;&amp;lt;/div&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&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;'row 2'&lt;/span&gt;&lt;span class="nt"&gt;&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;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'green-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Row 2, Column One
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'orange-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Row 2, Column Two
      &lt;span class="nt"&gt;&amp;lt;/div&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;class=&lt;/span&gt;&lt;span class="s"&gt;'column'&lt;/span&gt;&lt;span class="nt"&gt;&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;'blue-column'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Some Text in Row2, Column Three
      &lt;span class="nt"&gt;&amp;lt;/div&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&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/drews256/embed/WKPwxN?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Or we can resize our columns.&lt;/p&gt;

&lt;p&gt;To have a double column we can add a &lt;code&gt;.double-column&lt;/code&gt; class. This can work with any sized column though, you can do 60/40, you can do 10/10/10/10/10/10/10/10/10/10, honestly any combination is possible here. You can do 1 X 100. Or 10 x 1, then 20 x 3, then 30 x 1. The options are endless!&lt;/p&gt;

&lt;p&gt;On one layout, I added a large margin around my "column" and since the row will wrap down, I have one row with as many "columns" as I need. The columns were fixed width cards so they just wrap to the next line and flexbox neatly responsively wraps the cards down the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.double-column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-basis&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;flex&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/drews256/embed/djaMWj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This isn't very responsive though? We can add some responsitivity using media queries.&lt;/p&gt;

&lt;p&gt;Just move the &lt;code&gt;flex: 1&lt;/code&gt; and &lt;code&gt;flex: 2&lt;/code&gt; into a media-query (size depends on application I'm just giving an option)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex&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="nc"&gt;.double-column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &amp;gt; 800px: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqumjtleqvu3lrg6rmuoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqumjtleqvu3lrg6rmuoi.png" alt="large responsive at greater than 800" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At &amp;lt; 800px:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5410z8748ykuyxd1shs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5410z8748ykuyxd1shs5.png" alt="resonsive at less than 800" width="800" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final codepen, hint click the 1x or 0.5x buttons in the lower right corner to see the difference in the "responsive" layout.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/drews256/embed/bjzpvd?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Essentially we just blew the row/columns of bootstrap out of the water with 20 lines of CSS. We have a way to create row/column layouts quickly and since we use flexbox we barely have to worry about the layouts breaking, or anything going wrong. It's easily adapted to a wide variety of uses and allows a large amount of customizability. What do you think about flexbox? Have you tried it yet?&lt;/p&gt;

&lt;p&gt;Another hidden benefit is that if I layout React components in this way, its pretty easy to layout React-Native components very easily to look similar.&lt;/p&gt;

&lt;p&gt;I usually use SCSS in my projects so if you see something that isn't perfect CSS let me know! &lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Keeping API interactions sane while co-developing a React and React Native Application</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Wed, 08 Aug 2018 18:46:19 +0000</pubDate>
      <link>https://forem.com/drews256/keeping-api-interactions-sane-while-co-developing-a-react-and-react-native-application-3n3m</link>
      <guid>https://forem.com/drews256/keeping-api-interactions-sane-while-co-developing-a-react-and-react-native-application-3n3m</guid>
      <description>&lt;h1&gt;
  
  
  React and React-Native oh my!
&lt;/h1&gt;

&lt;h2&gt;
  
  
  A tale of two applications
&lt;/h2&gt;

&lt;p&gt;I recently worked on an application that had two separate public facing applications. One part was a web application, the other a phone application. Written in React and React-Native respectively. This use case is not that uncommon. It was fairly apparent from the get go that eventually we would need most features to be present in both the web application and the phone application. I figured we'd use Redux for state management but was pretty lost when it came to "interactions" with our back end from React and React-Native.&lt;/p&gt;

&lt;p&gt;The nice part is that its just Javascript so there is nothing fancy going on here. &lt;/p&gt;

&lt;p&gt;React-Native lifecycle hooks are the same and overall we wanted to make our mobile app experience much like our web app experience.&lt;/p&gt;

&lt;p&gt;After some research we decided to use Redux-Saga to make our lives easier and we were able to copy &lt;em&gt;most&lt;/em&gt; (if not all) of the api interaction between the two apps. There is even a thought that we may be able to get away with completely separating the API interaction from our views. Though, that may be a little more difficult than it sounds. Either way to the code examples!&lt;/p&gt;

&lt;h2&gt;
  
  
  But only a single backend
&lt;/h2&gt;

&lt;p&gt;Currently the back end is written in Rails and was built as an API only application. This lets us completely separate the back end and front ends. We were able to statically host all of our JS and CSS for the web application. We serve &lt;em&gt;zero&lt;/em&gt; html from our web server. How cool is that!? (I guess it's not that cool it is an SPA after all...) While the entire API does not follow the JSON API spec it does serve JSON API compliant JSON. Which I think adds a sense of consistency to our API. I'd like to continue to build out the JSON API compliance of the API but that is neither here nor there.&lt;/p&gt;

&lt;p&gt;Here is an example of a Saga that we used to interact with the back end. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/sagas/inventory.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;takeLatest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux-saga/effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ProductsEndpoint&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../persistance/ProductsEndpoint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// I use a custom Fetch implementation that wraps my requests to force&lt;/span&gt;
&lt;span class="c1"&gt;// authorization when required, it also forces JSON responses to be camelCased and // returns a consistent response from each request that we make.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../http/reduxFetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// This helper lets us have consistent reducer names, though it ended up not being perfect. &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defineTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INVENTORY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;inventories&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;// Each sagas exports actions and a reducer, which lets us combine them together // or use them across the app. Since they are all called 'actions' and 'reducer' // consistently it's super easy to import them at any point or from any component.&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getInventory&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REQUEST&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;toggleStock&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TOGGLE_STOCK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUEST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;started&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TOGGLE_STOCK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stocked&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FAILURE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SUCCESS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// JSON API led to some heavy selectors. It would be prudent to add an inventory // selector or an attribute selector, but add it to the list!&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inventory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inventory&lt;/span&gt;&lt;span class="dl"&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;inventories&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inventory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inventory&lt;/span&gt;&lt;span class="dl"&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;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;stocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inventory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inventory&lt;/span&gt;&lt;span class="dl"&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;stocked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;inventorySaga&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;products_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ProductsEndpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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;products_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUCCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;products_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FAILURE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;products_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
      &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FAILURE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;error&lt;/span&gt; 
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// All our top level Saga's are exported as a rootSaga. This lets us import and &lt;/span&gt;
&lt;span class="c1"&gt;// combine all of our sagas in a rootSaga file so we can keep everything &lt;/span&gt;
&lt;span class="c1"&gt;// organized and check name spacing.&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;rootSaga&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;takeLatest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inventoryTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inventorySaga&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;Overall this is pretty simple it looks like a Redux with some fancy yielding and generator functions. The most complicated thing going on above is the Saga itself. If you don't know too much about generator functions, you should check this guy out, it explains things nicely. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://davidwalsh.name/es6-generators"&gt;https://davidwalsh.name/es6-generators&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In simple terms though, they basically step through functions and allow you to call &lt;code&gt;.next()&lt;/code&gt; on them to run the function until it hits the next &lt;code&gt;yield&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;Redux Saga is a side effect library for Redux that runs generator functions based on action calls. So you don't have to have your business logic in your reducers. It lets you run any Saga on any call to any action, you can also use many types of effects that let you take just one Action (so you don't repeat the API call), or just the last action call (so you can call multiple times and it just takes the latest), there are many options but these are the basic ones.&lt;/p&gt;

&lt;p&gt;Here is some Redux-Saga documentation: &lt;a href="https://github.com/redux-saga/redux-saga"&gt;https://github.com/redux-saga/redux-saga&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another cool thing about Redux Saga is that your Component has to know nothing about it. Since your expose your selector to the Component via Redux. Convenient eh!? They're API calls that are not dependent on Component Lifecycle hooks* or call backs. In fact we could hydrate the entire store and then render views one at a time, and the components would be none the wiser. We can also call the API endpoint once and then just select out what we need from the response as we go instead of recalling the API multiple times.&lt;/p&gt;

&lt;p&gt;*Note at some point we do have to make API calls that have dependency's on components being rendered, but this &lt;em&gt;vastly&lt;/em&gt; reduces the amount of lifecycle hooking we are doing. I also tend to have parent components that I hook into and the children components then select out of the store that is filled based on the API call from the one lifecycle hook in the parent component.&lt;/p&gt;

&lt;p&gt;Now that we have the data how can we use this? Well, just need a connected component and all of a sudden we can pull data out of our selectors. &lt;/p&gt;

&lt;p&gt;In a strictly React application:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/components/Inventory.jsx&lt;/code&gt; and &lt;code&gt;src/components/Inventories.jsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Inventory&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attribute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;

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





&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;selectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../sagas/inventory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Inventories&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInventory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Inventory&lt;/span&gt; &lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;
}

// Since 'Inventories' is connected, any time we change inventories in our store,
//they're gonna get updated through the component. 

const mapStateToProps = (store) =&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; (&lt;span class="si"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;);

const mapDispatchToProps = (dispatch) =&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; bindActionCreators(actions, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(Inventories);

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



&lt;p&gt;Or in a React-Native Application&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/components/Inventory.jsx&lt;/code&gt; and &lt;code&gt;src/components/Inventories.jsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;View&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="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ScrollView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Inventory&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attribute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;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;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bindActionCreators&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;View&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="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ScrollView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;selectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../sagas/inventory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Inventory&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Inventory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Inventories&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInventory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Inventory&lt;/span&gt; &lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="si"&gt;}&lt;/span&gt;
}

// Since 'inventories' are connected, any time we change inventories in our store, they're gonna get updated through the component. 

const mapStateToProps = (store) =&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; (&lt;span class="si"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;);

const mapDispatchToProps = (dispatch) =&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; bindActionCreators(actions, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(Inventories);

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



&lt;p&gt;Almost the same exact components between React-Native and React. But all the business logic is thrown into the Saga so you don't have to worry too much about what is going on outside of the Component. Pretty easy to share the logic of the Saga and display what we want. &lt;/p&gt;

&lt;p&gt;Overall we run through a quick example of sharing API interactions between a React-Native and React application using Redux-Saga's. It allows you to build out React and React-Native apps quickly against a single API but encapsulate and share business logic between the two applications even though the Components are completely separate. &lt;/p&gt;

&lt;p&gt;*I'm by no means a JS/React or React Native expert. Let me know if you see anything funky or have any tips to help me elevate my JS or React game. &lt;/p&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Rails and the Single Responsibility Principle</title>
      <dc:creator>Andrew Stuntz</dc:creator>
      <pubDate>Thu, 02 Aug 2018 19:45:02 +0000</pubDate>
      <link>https://forem.com/drews256/rails-and-the-single-responsibility-principle-9kd</link>
      <guid>https://forem.com/drews256/rails-and-the-single-responsibility-principle-9kd</guid>
      <description>&lt;h1&gt;
  
  
  Single Responsibility Principle
&lt;/h1&gt;

&lt;p&gt;The Single Responsibility Principle states that: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"every module or class should have responsibility over a single part of the functionality provided by the software"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is fairly confusing though. Each module or class has responsibility for a single &lt;em&gt;part&lt;/em&gt; of &lt;em&gt;functionality&lt;/em&gt; of the software. Let's break this down a little bit. The functionality of software could be to display&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Robert_C._Martin"&gt;Robert C Martin&lt;/a&gt; says it this way,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A class should have only one reason to change."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is succinct. But complicated. A class should have only one &lt;em&gt;reason&lt;/em&gt; to change. What exactly is a reason, and what is a change to a class. There are many ways to change a class. We won't dwell on these questions right this second.&lt;/p&gt;

&lt;p&gt;How does Rails handle the single responsibility principle? How can we as developers uphold the single responsibility principle with the code that we write on top of the familiar Rails API?&lt;/p&gt;

&lt;p&gt;Rails models can be tough to extend. One of the tenets of writing good RoR is to keep controllers "skinny" and people often move that excess code back into the model. Models tend to get complicated, and they get complicated quickly. As you extend them to do different things, we have to test each method, as side affects pile up and we end up with unmanageable models. Which are arguably worse than unmanageable controllers.&lt;/p&gt;

&lt;p&gt;If you have been in the Rails community for almost any period of time I am sure you are familiar with the classic "Blog in 15 minutes Rails app". That allows you to post a new blog post and write comments about that blog post. It is simple and easy, traditionally, we have three models in this app. A &lt;code&gt;Post&lt;/code&gt;, a &lt;code&gt;Comment&lt;/code&gt;, and often a &lt;code&gt;User&lt;/code&gt; or an &lt;code&gt;Author&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The separation seems reasonable, in that there are two major &lt;em&gt;things&lt;/em&gt; we need to do here and something needs to &lt;em&gt;do&lt;/em&gt; those things.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Post&lt;/code&gt; is the blog post, it includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an author&lt;/li&gt;
&lt;li&gt;some information about when it was created&lt;/li&gt;
&lt;li&gt;the text about the blog post&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;Comment&lt;/code&gt; is just as simple it includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a reference to the post you are commenting on&lt;/li&gt;
&lt;li&gt;some information about the person that posted the comment&lt;/li&gt;
&lt;li&gt;the text of the comment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;Author/User&lt;/code&gt; that includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;identifying information&lt;/li&gt;
&lt;li&gt;magical rails associations that let you author comments and posts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The question here lies in what you expect the "Responsibility" here to be for each of these models. &lt;/p&gt;

&lt;p&gt;Is it the Rails models responsibility to know where it is stored? What data is associated with &lt;em&gt;itself&lt;/em&gt;. What to do you have a missing reference? What happens when each of the individual parts of a Comment or a Post are missing? How those models interact with other models.&lt;/p&gt;

&lt;p&gt;Or you could take a bigger view, and say that the "single" responsibility here is that the model handles &lt;em&gt;everything&lt;/em&gt; associated with the data that &lt;em&gt;is&lt;/em&gt; the model.&lt;/p&gt;

&lt;p&gt;We clearly know that it is &lt;em&gt;not&lt;/em&gt; the responsibility to handle viewing itself, or to handle serving itself up at the correct end point.&lt;/p&gt;

&lt;p&gt;Mr. Martin would say there should only be one reason for a Post or a Comment to change. What does this change entail? A change to the underlying data?&lt;/p&gt;

&lt;p&gt;Rails is pretty good at this. A quick example could entail, the reason being that we updated, created, read, or deleted an instance of this model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new_post = Post.new(user_id: 1, body: some_giant_wall_text)
# new_post is now a brand *new* instance of the Post class. 
new_post.save
# new_post is now persisted to the database
new_post.update(body: some_other_giant_wall_of_text)
# new_post is now persisted to the database again!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What just happened? The same instance of the class was persisted twice? Did we just break the SRP in three lines of Rails code?!&lt;/p&gt;

&lt;p&gt;We did in fact mutate the instance of the class. A quick sanity check does show that:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;new_post.body == some_other_giant_wall_of_text&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The instance of the class changed. The &lt;em&gt;reason&lt;/em&gt; behind this change was that we &lt;em&gt;persisted&lt;/em&gt; the data to the database.&lt;/p&gt;

&lt;p&gt;Why would rails do this? It sorta feels like the model was doing two things here. Saving and updating. But let me rephrase what it was doing. The Rails model was handling the CRUD operations to the database. That is all. We created the model, but passing in a hash of some attributes, which is succintly saved to the database, we then updated those attributes and it also succinctly saved these attributes to the database.&lt;/p&gt;

&lt;p&gt;The responsibility of the Rails model is to handle object in the database. The model allows developers to quickly handle CRUD operations against a database.&lt;/p&gt;

&lt;p&gt;I think its safe to say that the reason for changing is that the Rails model was persisting &lt;em&gt;something&lt;/em&gt; to the database. An attribute of the model changed, therefore the instance of the class can change and still fit the SRP.&lt;/p&gt;

&lt;p&gt;The point is, that in many cases Rails Models are consistent with the SRP and you don't have to be scared that you're Rails app is not SOLID outta the box.&lt;/p&gt;

&lt;p&gt;Our Rails Models inherit from ActiveRecord::Base for a reason. ActiveRecord handles the reading, creating, updating, and deleting of &lt;em&gt;objects&lt;/em&gt; in a database. Nothing else.&lt;/p&gt;

&lt;p&gt;But. &lt;/p&gt;

&lt;p&gt;Rails models are hard. They get complicated. We all know this. We have all broken a model in a Rail app with unexpected consequences.&lt;/p&gt;

&lt;p&gt;Often times business rules want our models to &lt;em&gt;do&lt;/em&gt; other things, &lt;em&gt;interact&lt;/em&gt; with other objects. When we want to allow our &lt;code&gt;Post&lt;/code&gt; to suddenly infer who the author of the &lt;code&gt;Post&lt;/code&gt; is. Or we want to only allow certain &lt;code&gt;User&lt;/code&gt;s to see and interact with certain &lt;code&gt;Comment&lt;/code&gt;s. Or a &lt;code&gt;Post&lt;/code&gt; suddenly needs to be a part of a posting board. Or you need to add &lt;code&gt;Tag&lt;/code&gt;s to a &lt;code&gt;Post&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rails does allow you to do this safely. It is quite trivial to add a new column to the database and create associations in the database safely. &lt;/p&gt;

&lt;p&gt;This is seen on our &lt;code&gt;Comment&lt;/code&gt;s and &lt;code&gt;Post&lt;/code&gt;s relationship.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;post = Post.find_by(author: 'me') # an instance of a post from our Database
post.comments # responds with a collection of Comments that match the Post ID
post.user # responds with an instance of the author
post.tags # responds with and error since we have no `Tag` table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again most of this is an extension of letting us persist information to our database. We still have yet to break the SRP. Furthermore, we were able to extend our model in many ways and not break SRP.&lt;/p&gt;

&lt;p&gt;We get pretty far doing this. Very far. &lt;/p&gt;

&lt;p&gt;Now what happens when you come back with a requirement like, each Author should have a summary of the Posts they have made. Easy enough you say.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;author = User.find_by(name: my_name) # find an author
author.posts # find all the posts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 I have a collection of posts for that Author. Easy peasy. &lt;/p&gt;

&lt;p&gt;Now I don't want the entire post! That is a lot of text for a summary. I only want the first 200 characters of each posts. Its a summary! &lt;/p&gt;

&lt;p&gt;We end up reaching for a method on &lt;code&gt;Post&lt;/code&gt; since that is the most convenient way to handle the data manipulation that must happen to display certain posts. &lt;/p&gt;

&lt;p&gt;We end with a method on the Post model like:&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;Post&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="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;body_summary&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&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;So to get a &lt;code&gt;Post&lt;/code&gt;s summary we can then call:&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;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&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;'my_name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# find an author&lt;/span&gt;
&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;posts&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;:body_summary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# grab all the post summaries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait! What is the goal of ActiveRecord and our Rails model!? To Read, Update, Create, and Delete information from a database! Not to transform the data that we retrieve from the database. &lt;/p&gt;

&lt;p&gt;Yes, now we have broken SRP in our Rails model. It was as simple as adding a single method. We have &lt;em&gt;now&lt;/em&gt; modified the instance of the object, for a reason besides reading, updating, creating, and deleting values from the database. The database will never see nor care about the &lt;code&gt;body_summary&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are mutating an object that exists for a single reason, CRUD in a database, so that we can use it in a very different way, to display that information in a particular way in the view. This is a very, very common pattern to do in Rails. It is bad. :slaps hand away from writing crappy methods:&lt;/p&gt;

&lt;p&gt;ActiveRecord models are for creating, reading, updating and deleting information from a database. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;body_summary&lt;/code&gt; itself is not the worst method in the world. My favorite Sandi Metz quote seemingly supports the existence of this code. In fact. I would encourage this effort in certain situations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The code is not perfect, but in some ways it achieves a higher&lt;br&gt;
standard: it is good enough."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this very simple example I would stop here and not really worry about it. Its not harming anything. Sure you could get into the N+1 query getting really heavy and taking a &lt;em&gt;long&lt;/em&gt; time to generate the &lt;code&gt;body_summary&lt;/code&gt; but chances are that is not a realistic possibility. &lt;/p&gt;

&lt;p&gt;Besides the point. The code does break the single responsibility rule that we have defined so far. What are some Rails like ways that we can clean this code up?&lt;/p&gt;

&lt;p&gt;Let's look at some other solutions so that our &lt;code&gt;Post&lt;/code&gt; doesn't break the single responsibility rule. Starting from simple implementations to more complex solutions.&lt;/p&gt;

&lt;p&gt;A simple and slightly better option here is to add a &lt;code&gt;body_summary&lt;/code&gt; column to the &lt;code&gt;Post&lt;/code&gt; table. Which would allow us to use a callback to add a &lt;code&gt;body_summary&lt;/code&gt; each time we updated the &lt;code&gt;Post&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;Post&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="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;after_save&lt;/span&gt; &lt;span class="ss"&gt;:build_body_summary&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_body_summary&lt;/span&gt;
    &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;body_summary: &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="o"&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 is pretty simple. Personally I like the simplicity here and it keeps all the information in a single table and for use on the &lt;code&gt;Post&lt;/code&gt; now when we want the body summaries we can access them like any other attribute on the &lt;code&gt;Post&lt;/code&gt; object. But we are also assuming &lt;em&gt;alot&lt;/em&gt; about the body here. What happens when &lt;code&gt;build_body_summary&lt;/code&gt; fails? We need two validation cycles just to save the &lt;code&gt;Post&lt;/code&gt; once.&lt;/p&gt;

&lt;p&gt;Thinking about the single responsibility principle with respect to this solution the &lt;code&gt;Post&lt;/code&gt; does save itself and all its own data. But does a &lt;code&gt;Post&lt;/code&gt; need to be responsible for saving itself!? Does the &lt;code&gt;Post&lt;/code&gt; class even have enough information to save itself each time? It must recall &lt;em&gt;itself&lt;/em&gt; and then change by transforming itself into something that is close to, but not quite itself. Did it change with the express purpose of creating, updating, deleting, or reading? No, it changed to generate a &lt;code&gt;body_summary&lt;/code&gt; that it &lt;em&gt;then&lt;/em&gt; persists to a database.&lt;/p&gt;

&lt;p&gt;Another option is to define a BodySummary class that is backed by a database table.&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;BodySummary&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="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:author&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 extend the &lt;code&gt;Post&lt;/code&gt; class with a callback. This would be a very Rails only way to keep the SRP in check. I would advocate the after_save callback because it would eliminate the need to overwrite the &lt;code&gt;initialize&lt;/code&gt; method or overwrite other methods on the Post 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;Post&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="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:body_summary&lt;/span&gt;

  &lt;span class="n"&gt;after_save&lt;/span&gt; &lt;span class="ss"&gt;:persist_body_summary&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perist_body_summary&lt;/span&gt;
    &lt;span class="no"&gt;BodySummary&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;post_id: &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;author_id: &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;summary: &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="o"&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 would allow us to use standard rails forms to build out a collection of summaries for each of the Posts. But there is now a pretty major dependency of &lt;code&gt;BodySummary&lt;/code&gt; smack dab in the middle of the &lt;code&gt;Post&lt;/code&gt; class. &lt;/p&gt;

&lt;p&gt;This is almost worse than having a &lt;code&gt;body_summary&lt;/code&gt; method on the &lt;code&gt;Post&lt;/code&gt; class. Heading back to the single responsibility one could in fact argue that it is outside the responsibility of the &lt;code&gt;Post&lt;/code&gt; class to persist anything in the database beyond &lt;em&gt;itself&lt;/em&gt; and therefore we are breaking SRP here too. I don't totally agree that &lt;code&gt;BodySummary&lt;/code&gt; itself constitutes an explicit break in the SRP if the intent of the &lt;code&gt;Post&lt;/code&gt; class is to create, read, update, and delete data for the Post. It just &lt;em&gt;happens&lt;/em&gt; to be in a different table than the &lt;code&gt;Post&lt;/code&gt;. What are your thoughts on this?&lt;/p&gt;

&lt;p&gt;A lot of people have talked about service objects lately. In fact there has been some recent kerfuffles by some heavy hitting Rails enthusiasts about the use of Service Objects. &lt;/p&gt;

&lt;p&gt;The discussion on Service Objects and their overall necessity in Rails is a topic for an entire other post with many people on all sides advocating for both options.&lt;/p&gt;

&lt;p&gt;I do enjoy a good service object and we could easily write a PORO that took a collection of &lt;code&gt;Post&lt;/code&gt;s and returns the body summary.&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;PostBodySummary&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:posts&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;posts&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&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;perform&lt;/span&gt;
    &lt;span class="n"&gt;summaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;posts&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;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;summaries&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;200&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;summaries&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;Calling &lt;code&gt;PostBodySummary.new(posts: user.posts).perform&lt;/code&gt; is very trivial and does the trick. One fall back here is we have a pretty limited understanding  as to what is exactly going on with this model. If we don't crack open the &lt;code&gt;PostBodySummary&lt;/code&gt; its pretty unapparent what we are doing. One way we can alleviate some of this pain is be renaming the &lt;code&gt;perform&lt;/code&gt; method to something like &lt;code&gt;generate_summaries&lt;/code&gt;. But ymmv in this simple case.&lt;/p&gt;

&lt;p&gt;This is easily reusable and extensible. We could define a different method and it would return shorter summaries. Easily enough we could write:&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;PostBodySummary&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shorter_summaries&lt;/span&gt;
    &lt;span class="n"&gt;posts&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;post&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;100&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="o"&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 is cleaner and follows our pattern above of explicitly returning what you call.&lt;/p&gt;

&lt;p&gt;We should check in on the single responsibility principle here for a moment. What does the &lt;code&gt;PostBodySummary&lt;/code&gt; class do? It takes an ActiveRecord Collection and returns the summaries of the bodies of the &lt;code&gt;Post&lt;/code&gt;s. When does the PostBodySummary change? It only changes when we want a different ActiveRecord collection of Posts. This seems reasonable. One &lt;code&gt;Author&lt;/code&gt;'s set of &lt;code&gt;Post&lt;/code&gt;s is different than a different set &lt;code&gt;Author&lt;/code&gt;'s &lt;code&gt;Posts&lt;/code&gt; and each instance of the service object would reflect this change. This solution is probably the solution that I think follows the single responsibility principle the most. &lt;code&gt;PostBodySummary&lt;/code&gt; changes when the collection of &lt;code&gt;Posts&lt;/code&gt; changes. The one reason to change, is that the collection of Posts changes and this object reflects this. &lt;/p&gt;

&lt;p&gt;Overall thinking about the single responsibility principle is tough. Especially when we are ready to extend the API that Rails exposes for us. Some people would say that I missed the point by talking too much about Rails models and that the single responsibility principle isn't so much about instances of objects and the way we use them as it is about the &lt;em&gt;actual&lt;/em&gt; code in the class. I digress.&lt;/p&gt;

&lt;p&gt;Rails models are tough to extend when they get complicated, part of the reasoning is that they continually break the single responsibility rule even in simple cases. Generally the goal of a Rails model is to handle the data and the database. I think this is complicated enough that, that is &lt;em&gt;all&lt;/em&gt; it needs to do. &lt;/p&gt;

&lt;p&gt;There are many options that let us encapsulate the complications of Rails models outside of the actual model. Some of my favorite are presented including utilizing callbacks to set an attribute on itself, using a Service object to completely remove the code, adding a new ActiveRecord object itself, and some options that fit in between. &lt;/p&gt;

&lt;p&gt;Let me know what you think about the single responsibility principle!&lt;/p&gt;

&lt;p&gt;-------- Some more commentary on the SRP by the man himself:&lt;/p&gt;

&lt;p&gt;Uncle Bob's clarification to what he actually meant when he said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A class should have only one reason to change."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://8thlight.com/blog/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html"&gt;https://8thlight.com/blog/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-------- Some more info on Service Objects and the articles I mentioned earlier:&lt;/p&gt;

&lt;p&gt;Aaron Lasseigne's piece on using service objects in response to Avdi's post: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://aaronlasseigne.com/2017/11/08/why-arent-we-using-more-service-objects-already/"&gt;https://aaronlasseigne.com/2017/11/08/why-arent-we-using-more-service-objects-already/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avdi Grimm's piece on service objects and procedures: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://avdi.codes/service-objects/"&gt;https://avdi.codes/service-objects/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing to note, Aaron is the author of a very popular Service Object gem called ActiveInteraction. So that could possibly jade his overall opinion.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>solid</category>
    </item>
  </channel>
</rss>
