<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zee</title>
    <description>The latest articles on DEV Community by Zee (@zspencer).</description>
    <link>https://dev.to/zspencer</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%2F106105%2F8aa5facd-a2d4-4a7e-b331-8998a3acb3c4.jpeg</url>
      <title>DEV Community: Zee</title>
      <link>https://dev.to/zspencer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zspencer"/>
    <language>en</language>
    <item>
      <title>Surprising behavior with Rails I18n Lazy Lookups and Partials</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Thu, 16 May 2019 02:12:53 +0000</pubDate>
      <link>https://dev.to/zspencer/surprising-behavior-with-rails-i18n-lazy-lookups-and-partials-5g78</link>
      <guid>https://dev.to/zspencer/surprising-behavior-with-rails-i18n-lazy-lookups-and-partials-5g78</guid>
      <description>&lt;p&gt;Rails i18n supports &lt;a href="https://guides.rubyonrails.org/i18n.html#lazy-lookup"&gt;lazy lookup&lt;/a&gt; so that if you have a locales file shaped like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;newsletter_cta&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stay&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Up&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;To&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Date&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;With&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Latest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Happenings"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can reference the value in &lt;code&gt;newsletter_cta.title&lt;/code&gt; via &lt;code&gt;t('.title')&lt;/code&gt; when in &lt;code&gt;app/views/_newsletter_cta.html.erb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In many Rails applications, I extract partials to encapsulate components so any HTML/CSS oriented team member doesn't have to learn how to write helper methods when writing components; and so we don't wind up with copy-pasted code that would necessitate massive pain when upgrading or shifting the underlying UI framework.&lt;/p&gt;

&lt;p&gt;Here's an example of a bootstrap section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/application/_section.html.erb
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"section-wrap"&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;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="cp"&gt;%&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;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which I can then use in other views, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/_newsletter_cta.html.erb
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"section"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'newsletter_cta.title'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"cta-button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'newsletter_cta.title'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; 
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This works fine, so long as don't rely on lazy lookup and I hardcode the full lookup string for the translation.&lt;/p&gt;

&lt;p&gt;However, as soon as I use lazy lookups the translation is not found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/_newsletter_cta.html.erb 
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"section"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.title'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"cta-button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;text: &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.title'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; 
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Tracing through to &lt;a href="https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/actionview/lib/action_view/helpers/translation_helper.rb#L87"&gt;the translate method in ActionView::Helpers::TranslationHelper&lt;/a&gt; we see that it relies on a method named &lt;a href="https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/actionview/lib/action_view/helpers/translation_helper.rb#L126"&gt;&lt;code&gt;scope_key_by_partial&lt;/code&gt;&lt;/a&gt; which in turn, relies on an instance variable &lt;code&gt;@virtual_path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Do any of y'all know of a way to preserve the virtual path while using partials with blocks? Is there a gem somewhere that changes this behavior? I'm a bit surprised no one else has stumbled into this.&lt;/p&gt;

</description>
      <category>help</category>
      <category>rails</category>
      <category>i18n</category>
    </item>
    <item>
      <title>Episode 2 of #DebuggingDowntime is live-streaming tomorrow!</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Tue, 29 Jan 2019 01:46:37 +0000</pubDate>
      <link>https://dev.to/cohere/episode-2-of-debuggingdowntime-is-live-streaming-tomorrow-3ip8</link>
      <guid>https://dev.to/cohere/episode-2-of-debuggingdowntime-is-live-streaming-tomorrow-3ip8</guid>
      <description>&lt;p&gt;Hey everybody!&lt;/p&gt;

&lt;p&gt;Two weeks ago, we live-streamed a mob-programming session where we attempted to debug and resolve a recurring downtime problem for Notabli. Unfortunately, it didn't completely work, so we're heading back into the trenches tomorrow at 10 AM PT / 1 PM ET. You can &lt;a href="https://public.aerialspac.es/Pair%20Programming%20Live%20Streams?eventId=c47576bb-cb1e-41b9-b582-bcb95a50e69c"&gt;join us for the live-session tomorrow&lt;/a&gt;, or you can wait until we publish the uncut version to our &lt;a href="https://www.youtube.com/channel/UCGxJ6esUxAGYyu5Eok65Qgw"&gt;RealWorldCode YouTube channel&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;While all live-streamed, un-cut and rough-cut sessions will be available free to stream, we want to be able to afford to hire a professional video editor to tighten everything up and help us highlight the diamond in the rough.&lt;/p&gt;

&lt;p&gt;To finance that, we are selling a &lt;a href="https://gum.co/rwc-s1-debugging-downtime"&gt;Debugging Downtime season pass&lt;/a&gt;. This gives you the rights to download the uncut, rough cut, and gold cut content, as well as funds our hiring of a rough cut of each of the sessions and if we can make enough to justify it, a tight cut that highlights the diamonds in the rough.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>rails</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Is there a drop in JS Array-compatible React/Vue-friendly linked-list library?</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Mon, 21 Jan 2019 23:30:55 +0000</pubDate>
      <link>https://dev.to/zspencer/is-there-a-drop-in-js-array-compatible-reactvue-friendly-linked-list-library-hh8</link>
      <guid>https://dev.to/zspencer/is-there-a-drop-in-js-array-compatible-reactvue-friendly-linked-list-library-hh8</guid>
      <description>&lt;p&gt;Hello friends!&lt;/p&gt;

&lt;p&gt;I have the following collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;messages&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="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="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which I sometimes map over, unshift, and push onto. It hit me... 2 months ago... that &lt;em&gt;really&lt;/em&gt; it needs to be a linked list, and not an array; as sometimes I need to render different things depending on information in the next or previous message.&lt;/p&gt;

&lt;p&gt;I've procrastinated swapping out the collection's underlying data structure because most of the bugs were not related to the array-ness of the messages collection.&lt;/p&gt;

&lt;p&gt;However I'm finally butting up against some bugs (again) where swapping the array for a linked list seems like the most reasonable course of action.&lt;/p&gt;

&lt;p&gt;Do any of you know about a drop-in JavaScript Array replacement that plays nicely with the reactive nature of Vue 2?&lt;/p&gt;

&lt;p&gt;I've found that most drop-in replacements don't take into account that Vue requres you to use &lt;code&gt;Vue.set&lt;/code&gt; when adding new properties to an Object; and most LinkedList implementations use an object as their underlying data structure.&lt;/p&gt;

&lt;p&gt;Any help would be greatly appreciated!&lt;/p&gt;

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

</description>
      <category>help</category>
      <category>vue</category>
      <category>javascript</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Lessons Learned from our First #RealWorldCode Live Stream</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Mon, 14 Jan 2019 18:31:21 +0000</pubDate>
      <link>https://dev.to/cohere/lessons-learned-from-our-first-realworldcode-live-stream-470i</link>
      <guid>https://dev.to/cohere/lessons-learned-from-our-first-realworldcode-live-stream-470i</guid>
      <description>&lt;p&gt;Earlier this month, Betsy and I tried to live stream a pairing session and &lt;em&gt;failed miserably&lt;/em&gt;. But! We had fun, so we spent about 30m performing a &lt;a href="http://www.funretrospectives.com/the-retrospective-prime-directive/"&gt;retrospective&lt;/a&gt; on our &lt;a href="https://www.wecohere.com/events/january-3rd-2019-livestream-of-debugging-a-race-condition-in-a-cypress-browser-test/?utm_campaign=real-world-code-2019-01-17-lessons-learned"&gt;&lt;em&gt;Debugging a Race Condition in Cypress&lt;/em&gt; event&lt;/a&gt;. To &lt;a href="https://redsquirrel.com/dave/work/a2j/patterns/ExposeYourIgnorance.html"&gt;expose our ignorance&lt;/a&gt; (and therefore, learn more!) we've made our &lt;a href="https://drive.google.com/open?id=1CnFlF4UOheK4lnwgOAqJFiz6BKS8RFDG4hzk7YzTv-g"&gt;retro notes&lt;/a&gt; available to you.&lt;/p&gt;

&lt;p&gt;The joy of retrospectives is we now have a plan to make the stream flow much more smoothly! The angst of retrospectives is that it can be touch to talk about failure-modes in a positive, proactive way. Thankfully though our years of working together, we've developed &lt;a href="https://hbr.org/2017/08/high-performing-teams-need-psychological-safety-heres-how-to-create-it"&gt;a level of physchological safety&lt;/a&gt; that allows us to have healthy, reflective conversations.&lt;/p&gt;

&lt;h4&gt;
  
  
  How We'll Do Better Next Session
&lt;/h4&gt;

&lt;p&gt;The next live-streaming sessions are going to be &lt;em&gt;longer&lt;/em&gt;! Yes, &lt;em&gt;Longer&lt;/em&gt;. The main reason for this is we realized that due to A) us having solved the problem before and B) us having logistical difficulties, we didn't get much of an opportunity to enter a state of productive flow.&lt;/p&gt;

&lt;p&gt;We know this may be… boringer for you, but we're hoping it will allow us to dive deeper into insights in a more casual manner; so we can pull out the diamonds in the rough out and provide them in more easily accessible chunks.&lt;/p&gt;

&lt;p&gt;We're also planning to have someone who is &lt;em&gt;not&lt;/em&gt; streaming monitoring the &lt;a href="https://twitter.com/search?q=%23RealWorldCode&amp;amp;src=typd"&gt;#RealWorldCode hashtag&lt;/a&gt; and mentions to &lt;a href="https://twitter.com/wecohere"&gt;Cohere's Twitter account&lt;/a&gt; to make sure that if logistical issues arise we can resolve them in a timely and inclusive manner.&lt;/p&gt;

&lt;h4&gt;
  
  
  How You Can Attend our Next Livestream
&lt;/h4&gt;

&lt;p&gt;To attend our next livestream, &lt;a href="https://www.wecohere.com/events/january-15th-2019-10am-pt-1pm-et-real-world-code-livestream-preventing-cascade-failures-caused-by-excessive-concurrent-writes-in-a-heroku-hosted-amazon-rds-backed-ruby-on-rails-app/?utm_campaign=real-world-code-2019-01-17-lessons-learned"&gt;&lt;em&gt;Preventing Cascade Failures Caused by Excessive Concurrent Writes&lt;/em&gt; on January 15th, from 10 AM PT / 1 PM ET to 12 PM PT / 3PM ET&lt;/a&gt;, you &lt;a href="https://public.aerialspac.es/Pair%20Programming%20Live%20Streams?eventId=d38e706b-9b2c-47fa-a90a-afa5ff1f8724"&gt;may join the stream directly&lt;/a&gt;. You may also &lt;a href="https://www.eventbrite.com/e/real-world-code-preventing-excessive-concurrent-write-induced-cascade-failures-in-ruby-on-rails-tickets-54729746144?utm_campaign=real-world-code-2019-01-17-lessons-learned"&gt;RSVP via EventBrite&lt;/a&gt; by tomorrow, January 15th at 8 AM PT/ 11AM ET. This &lt;em&gt;should&lt;/em&gt; work better than the horrible form I put on the website last time.&lt;/p&gt;

&lt;p&gt;Once again, you should be able to &lt;a href="https://public.aerialspac.es/Pair%20Programming%20Live%20Streams?eventId=d38e706b-9b2c-47fa-a90a-afa5ff1f8724"&gt;bookmark this link&lt;/a&gt; which will give you a window into the stream when we go live.&lt;/p&gt;

&lt;p&gt;We're looking forward to seeing you there! If it doesn't work or you have any questions, reply to this post or tweet at Cohere with the hashtag #RealWorldCode and we'll do our best to get you in quickly &amp;lt;3.&lt;/p&gt;

&lt;p&gt;Zee, Betsy and Jackson&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>learning</category>
      <category>devops</category>
      <category>rails</category>
    </item>
    <item>
      <title>Incident Report! How We Used the OODA Loop to Triage a Amazon RDS Cascade Failure for a Heroku-hosted Rails App </title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Thu, 10 Jan 2019 23:48:04 +0000</pubDate>
      <link>https://dev.to/cohere/incident-report-how-we-used-the-ooda-loop-to-triage-a-amazon-rds-cascade-failure-for-a-heroku-hosted-rails-app--54bd</link>
      <guid>https://dev.to/cohere/incident-report-how-we-used-the-ooda-loop-to-triage-a-amazon-rds-cascade-failure-for-a-heroku-hosted-rails-app--54bd</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally posted on &lt;a href="https://www.wecohere.com%5D"&gt;Cohere's Website&lt;/a&gt; with the title &lt;a href="https://www.wecohere.com/articles/how-we-used-the-ooda-loop-to-triage-a-cascade-failure-for-a-heroku-hosted-aws-rds-backed-ruby-on-rails-app.html"&gt;Real World Code Incident Report! How We Used the OODA Loop to Triage a Amazon RDS Cascade Failure for a Heroku-hosted Rails App&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.notabli.com"&gt;Notabli&lt;/a&gt; reached out to us the day after Christmas because their API was falling over. Their Friendly Engineer, &lt;a href="https://www.linkedin.com/in/ty-rauber-69822b5/"&gt;Ty Rauber&lt;/a&gt; was working hard with their Head of Product, &lt;a href="https://www.linkedin.com/in/jacksonlatka"&gt;Jackson Latka&lt;/a&gt;, to bring it back up. They wanted a second pair of programmer-eyes, and I was bored. Isn't that what everyone uses the Holidays for? Triaging production issues for fun?&lt;/p&gt;

&lt;h3&gt;
  
  
  Triaging a Cascade Failure - OODA Loop 1 - Maybe it's the N+1 Query?
&lt;/h3&gt;

&lt;p&gt;We hopped into a &lt;a href="https://zoom.us/"&gt;Zoom&lt;/a&gt; room together with their Product Lead, &lt;a href="https://www.linkedin.com/in/jacksonlatka/"&gt;Jackson Latka&lt;/a&gt; and began the debugging process. When debugging, I try to consciously follow the &lt;a href="https://en.wikipedia.org/wiki/OODA_loop"&gt;OODA loop&lt;/a&gt;: Observe, Orient, Decide and Act.&lt;/p&gt;

&lt;p&gt;First, &lt;em&gt;Observe!&lt;/em&gt; Jackson granted me rights to manage their &lt;a href="https://aws.amazon.com/rds/"&gt;Amazon Relational Database Service (RDS)&lt;/a&gt;, &lt;a href="https://www.heroku.com/"&gt;Heroku&lt;/a&gt; for hosting, &lt;a href="https://papertrailapp.com/"&gt;Papertrail&lt;/a&gt; for logging and &lt;a href="https://scoutapp.com/"&gt;Scout&lt;/a&gt; for application monitoring. Huge shout-out to Jackson for the trust he granted, it's the main reason this worked so well. Teams move at the speed of trust, after all!&lt;/p&gt;

&lt;p&gt;Second, &lt;em&gt;Orient!&lt;/em&gt; I began exploring the architecture with Ty. Notabli's API is a &lt;a href="https://rubyonrails.org/"&gt;Ruby on  Rails&lt;/a&gt; application hosted on Heroku. It uses &lt;a href="https://sidekiq.org/"&gt;Sidekiq&lt;/a&gt; to perform background jobs, many of which are computationally intensive as they process video and images. Further, the main endpoint is a stream of &lt;em&gt;Moments&lt;/em&gt;, which is resolved by a number of data fetching operations, one of which is an &lt;a href="https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/"&gt;N + 1 Query&lt;/a&gt;, the other of which is a massive cross-table inner join.&lt;/p&gt;

&lt;p&gt;Third, &lt;em&gt;Decide!&lt;/em&gt; Being (primarily) a programmer, I immediately decided to attempt to resolve the problem via code.&lt;/p&gt;

&lt;p&gt;Fourth, &lt;em&gt;Act!&lt;/em&gt; Because they use the &lt;a href="https://github.com/cerebris/jsonapi-resources"&gt;jsonapi-resources&lt;/a&gt; gem, a library I'm personally unfamiliar with, it was difficult to find a seam to begin resolving the query problems. Ty and I blundered through, poking and prodding; but after 15 minutes, we decided this was a dead-end for now.&lt;/p&gt;

&lt;p&gt;On reflection, I realized I had violated one of my principles of effective downtime recovery: &lt;em&gt;Attempt to solve downtime first at the infrastructure level before making changes that would require a redeploy of the application.&lt;/em&gt;  I don't follow this as hard and fast, but it's served me well over the decades.&lt;/p&gt;

&lt;h3&gt;
  
  
  OODA Loop 2 - Maybe it's the disk?
&lt;/h3&gt;

&lt;p&gt;We went back to &lt;em&gt;observing&lt;/em&gt; the AWS dashboard and what did I find? The first major outage had happened when the system ran of storage space!&lt;/p&gt;

&lt;p class="tc"&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jmDI_DCF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u23uswmqu4ckzn3s52ep.png" class="article-body-image-wrapper"&gt;&lt;img alt="The RDS Instance's Free storage space's Precipitous Decline" src="https://res.cloudinary.com/practicaldev/image/fetch/s--jmDI_DCF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u23uswmqu4ckzn3s52ep.png"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;After briefly kicking myself for not noticing that sooner; I dug in a bit further and discovered that right before failure, the server was running at the maximum &lt;a href="https://en.wikipedia.org/wiki/IOPS"&gt;IOPS&lt;/a&gt; it could provide! IOPS, for those whose eyes glaze over when they see a Wikipedia article, is the throughput of read and write disk operations.&lt;/p&gt;

&lt;p&gt;Diving in deeper we realized it was the &lt;em&gt;write operations&lt;/em&gt; that were using up all the IOPS. I resumed kicking myself for wasting ~30 minutes on mitigating a &lt;em&gt;read&lt;/em&gt; related issue; and proposed a two-step bandaid:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We upgrade the disk, as &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html"&gt;AWS grants IOPS based on disk-size&lt;/a&gt;; this should bring the application back up and allow it to handle the increased traffic.&lt;/li&gt;
&lt;li&gt; Once the system is stable, we look for why there are so many writes and determine if any of them can be removed from the system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This, thankfully, worked. The application came back up! It maintained a reasonable degree of performance, and there was a reproducible way to bring the application back up in the event the database fell over again.&lt;/p&gt;

&lt;p&gt;Unfortunately, because all their work is done on a volunteer basis; we ran out of time to perform step 2!&lt;/p&gt;

&lt;p&gt;To help fund the time we're going to spend fixing it, we're offering our friends and followers the opportunity to join us &lt;em&gt;free&lt;/em&gt; or &lt;em&gt;with donation&lt;/em&gt; on January 15th, 2019 at 10AM PT/1PM ET for a two hour &lt;em&gt;&lt;a href="https://www.wecohere.com/events/january-15th-2019-10am-pt-1pm-et-real-world-code-livestream-preventing-cascade-failures-caused-by-excessive-concurrent-writes-in-a-heroku-hosted-amazon-rds-backed-ruby-on-rails-app/?utm_campaign=real-world-code-2019-01-17"&gt;Real World Code Livestream: Preventing Excessive Concurrent Write Induced Cascade Failures in a Heroku Hosted, Amazon RDS Backed Ruby on Rails App&lt;/a&gt;&lt;/em&gt;. We're not sure if we'll fully diagnose and debug why the write cascade happens in a single session, so we may have a second session on January 29th at the same time!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>heroku</category>
      <category>aws</category>
      <category>debugging</category>
    </item>
    <item>
      <title>What do you think of Visual Studio Live Share for multi-user editing?</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Sat, 05 Jan 2019 17:18:28 +0000</pubDate>
      <link>https://dev.to/zspencer/what-do-you-think-of-visual-studio-live-share-for-multi-user-editing-45m8</link>
      <guid>https://dev.to/zspencer/what-do-you-think-of-visual-studio-live-share-for-multi-user-editing-45m8</guid>
      <description>&lt;p&gt;Hello Dev.Tovians!&lt;/p&gt;

&lt;p&gt;I'm looking into &lt;a href="https://visualstudio.microsoft.com/services/live-share/"&gt;Visual Studio Live Share&lt;/a&gt; as a mechanism for doing some lower-latency remote pairing without requiring as much screen sharing. (which is slow!)&lt;/p&gt;

&lt;p&gt;I'm curious what (if any) challenges you encountered while:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting it up &lt;/li&gt;
&lt;li&gt;Granting access&lt;/li&gt;
&lt;li&gt;Using it for extended periods of time&lt;/li&gt;
&lt;li&gt;Adding new people/dropping people in the middle of the session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the past, I've tried similar tools and the issues I've run into were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Oh no it stopped working but it was invisible"&lt;/li&gt;
&lt;li&gt;"Wait, the test failed? Oh! They were running against the version that hadn't been updated yet!"&lt;/li&gt;
&lt;li&gt;"Yeah, all the code isn't on your computer yet because we haven't opened them in the editor..."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My hope is that with Microsoft (who has traditionally been very good at dev tooling) throwing their weight behind a multi-user-code-editor it'll be a touch more consistently usable than the well-intentioned but underfunded hobby-projects I've used in the past.&lt;/p&gt;

&lt;p&gt;Any experiences you can share or tips for a noobie getting started with it?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>tips</category>
      <category>editors</category>
      <category>help</category>
    </item>
    <item>
      <title>Cohere 2018 Year in Review - Reflections on Financial, Mental, Physical and Relational Health</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Thu, 03 Jan 2019 00:44:09 +0000</pubDate>
      <link>https://dev.to/cohere/cohere-2018-year-in-review---reflections-on-financial-mental-physical-and-relational-health-5bpl</link>
      <guid>https://dev.to/cohere/cohere-2018-year-in-review---reflections-on-financial-mental-physical-and-relational-health-5bpl</guid>
      <description>&lt;p&gt;In 2017, &lt;a href="https://twitter.com/jtu"&gt;Jennifer&lt;/a&gt;, &lt;a href="https://twitter.com/betsythemuffin"&gt;Betsy&lt;/a&gt;, and I were burnt out. Over the past two years, we had grown an engineering team from 4 to 40 and extended a product that served hundreds of smaller lending institutions to be the first production, cloud-hosted software as a service at one of the largest banks in America.&lt;/p&gt;

&lt;p&gt;We'd done the near impossible and made lifelong friends while doing it. More importantly, we had launched the careers of a dozen first-time developers. But, as the dust settled, we had to ask ourselves: was it worth it?&lt;/p&gt;

&lt;p&gt;It took sacrifice. I gained 30 pounds. My significant other and I broke up. My alcohol use skyrocketed. I lost touch with friends and lacked &lt;a href="https://butyoudontlooksick.com/articles/written-by-christine/the-spoon-theory/?utm_campaign=cohere-2018-year-in-review"&gt;the spoons&lt;/a&gt; to keep those friendships active.&lt;/p&gt;

&lt;p&gt;So we took a step back and concluded that while our &lt;em&gt;financial&lt;/em&gt; health was important, our &lt;em&gt;physical, mental, and relational health&lt;/em&gt; was just as important.&lt;/p&gt;

&lt;h4&gt;
  
  
  Origins - Q4 2017 - Formation as a Worker Cooperative
&lt;/h4&gt;

&lt;p&gt;In Q4 2017, we formed a three-person &lt;a href="https://institute.coop/what-worker-cooperative?utm_campaign=cohere-2018-year-in-review"&gt;worker cooperative&lt;/a&gt;. Our goals were to build an environment where we each could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Be financially comfortable without compromising our relationships, health, and happiness.&lt;/li&gt;
&lt;li&gt;  Make space for the things we value outside of work.&lt;/li&gt;
&lt;li&gt;  Do work we &lt;em&gt;enjoy&lt;/em&gt; doing.&lt;/li&gt;
&lt;li&gt;  Develop long-term financial security independent of client-work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initially, we were a bog-standard technology agency. My experience running &lt;a href="https://www.zincma.de/?utm_campaign=cohere-2018-year-in-review"&gt;Zinc&lt;/a&gt; and Cognizance, and working for &lt;a href="https://www.leandog.com/?utm_campaign=cohere-2018-year-in-review"&gt;Leandog&lt;/a&gt; and &lt;a href="https://pillartechnology.com/?utm_campaign=cohere-2018-year-in-review"&gt;Pillar Technology&lt;/a&gt;, made tech consulting an easy way to generate revenue. The structure was similar to many small agencies: three partners; a few subcontractors.&lt;/p&gt;

&lt;p&gt;In Q4 2017, we earned almost what it would take for us to be financially comfortable. All of our revenue came from shipping features for clients.&lt;/p&gt;

&lt;p&gt;My physical health improved. I dropped 10 pounds. My non-work relationships improved. My relationships with my co-founders were mostly solid, with some cracks that were easily attributable to our stage in the &lt;a href="https://en.wikipedia.org/wiki/Tuckman%27s_stages_of_group_development?utm_campaign=cohere-2018-year-in-review"&gt;norming, forming, storming, performing cycle&lt;/a&gt; and us being crispy from our previous job.&lt;/p&gt;

&lt;p&gt;We liked getting paid to do good work on hard problems for our clients. But it was impossible to pursue our goal of long-term financial freedom independent of client-work. We were three people billing as near-to-full-time as possible, after all! Further, the lack of slack in our work schedules meant we didn't really get much opportunity to work &lt;em&gt;together&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So we began to experiment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 0 - Can We Make a Viable Product?
&lt;/h4&gt;

&lt;p&gt;In December, we went on "break" and attempted to launch an open-source, crowd-funding SAAS: &lt;a href="https://github.com/wecohere/nourish.party?utm_campaign=cohere-2018-year-in-review"&gt;Nourish&lt;/a&gt;. &lt;strong&gt;&lt;em&gt;It didn't work&lt;/em&gt;&lt;/strong&gt;. We built a proof of concept; but lacked the marketing, design, and business development chops to make &lt;a href="https://medium.com/precoil/the-perils-of-crowdfunded-innovation-10a812be649f?utm_campaign=cohere-2018-year-in-review"&gt;it desirable and viable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since we could build a proof of concept with a small budget why not sell that to people who do have the product marketing and business development chops? Try to get a cut of the equity pie on the same terms as the seed and angel investors?&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 1 - Q1 2018 - Angel/Seed Startup Launcher
&lt;/h4&gt;

&lt;p&gt;We began by pitching to a few friends and colleagues who had pre-seed startups. We offered a severely restricted rate in exchange for a piece of the equity. No one gave us equity, but we launched both &lt;a href="https://hellowalden.com/?utm_campaign=cohere-2018-year-in-review"&gt;Walden&lt;/a&gt; and &lt;a href="https://blockbutler.io/?utm_campaign=cohere-2018-year-in-review"&gt;BlockButler&lt;/a&gt; in Q1 2018 for clients who wanted MVPs.&lt;/p&gt;

&lt;p&gt;Meanwhile, Jennifer started pitching training and coaching to startups on &lt;a href="https://www.wecohere.com/products/interviewer-skills-for-engineers-and-hiring-managers/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=exp-1-how-do-eng-hiring-and-interviewing"&gt;how to do engineering, hiring, and interviewing&lt;/a&gt; and landed our first training client.&lt;/p&gt;

&lt;p&gt;But again we saw a high cost to our personal health, happiness, and relationships with one another.&lt;/p&gt;

&lt;p&gt;The stakes were too high.&lt;/p&gt;

&lt;p&gt;Our clients were relying on us to launch their businesses; most of which were funded via friends and family rounds or their own investment. It was difficult for us to bill at a reasonable rate when the hours necessary to adapt to their learnings rose.&lt;/p&gt;

&lt;p&gt;So we cut our rates and fell prey to a common failure mode of good consultants: prioritizing other people's needs too far above our own.&lt;/p&gt;

&lt;p&gt;Our revenues per-hour billed declined precipitously. Yet we learned two things!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1.&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;We find more joy in helping teams solve a particular social or technical challenge than typing code.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2. When we do type code, we'd rather do it for stable nonprofits, and for businesses where we're getting paid out of revenues, not retirement accounts, or capital investments.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Q1 2018 we earned just over what we consider enough revenue to be sustainable, 28% of income came from training and coaching, and 62% of it from typing code. Unfortunately, due to how unbalanced our time for dollars rate was; we compromised our goal to leave space for the things we enjoy outside of work. As a result, my physical activity levels were way down and I began gaining weight again. Further, our working relationships suffered.&lt;/p&gt;

&lt;p&gt;Something had to change.&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 2 - Q2 2018 - Technical Training and eCourses
&lt;/h4&gt;

&lt;p&gt;This time we pivoted our market towards mature startups, bootstrapped revenue-positive businesses and nonprofits that seek to fill a niche instead of dominating a market.&lt;/p&gt;

&lt;p&gt;This was challenging. First, most technology companies want full-time employees or contractors. We don't want to be full-time &lt;em&gt;anything&lt;/em&gt;. Second, bootstrapped, revenue-positive businesses need a pretty significant pain-point in order to justify spending. So we diversified.&lt;/p&gt;

&lt;p&gt;Jennifer spearheaded the work of developing a sales and marketing practice for our myriad of &lt;a href="https://www.wecohere.com/products/technical-coaching/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-coaching-1"&gt;coaching&lt;/a&gt; and &lt;a href="https://www.wecohere.com/products/interviewer-skills-for-engineers-and-hiring-managers/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-other"&gt;other&lt;/a&gt; &lt;a href="https://www.wecohere.com/products/real-world-refactoring/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-training"&gt;training&lt;/a&gt; &lt;a href="https://www.wecohere.com/products/pairing-with-privilege/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-offerings"&gt;offerings&lt;/a&gt;. Betsy recorded and launched our first video course, &lt;a href="https://avdi.codes/moom/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-moom"&gt;Mastering the Object Oriented Mindset&lt;/a&gt; and took full responsibility for our &lt;a href="https://www.wecohere.com/products/technical-coaching/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-technical-coaching"&gt;technical coaching&lt;/a&gt; clients.&lt;/p&gt;

&lt;p&gt;We made our first public offerings of our &lt;a href="http://railsconf.org/program/workshops#session-560?utm_campaign=cohere-2018-year-in-review"&gt;Interviewer Skills&lt;/a&gt; and &lt;a href="http://railsconf.org/program/workshops#session-554?utm_campaign=cohere-2018-year-in-review"&gt;Real-World Refactoring&lt;/a&gt; workshops at RailsConf and the public launch of our &lt;a href="https://www.wecohere.com/products/pairing-with-privilege/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp2-pairing-with-privilege"&gt;Pairing with Privilege&lt;/a&gt; workshop at &lt;a href="https://pearconf.splashthat.com/?utm_campaign=cohere-2018-year-in-review"&gt;PearConf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This shifted both Jennifer and Betsy's time and attention investment towards primarily non-billable activities. In order for us to keep making rent, I focused on typing code for existing clients and following up on leads for new delivery projects.&lt;/p&gt;

&lt;p&gt;At the end of Q2 2018, our revenues had slipped to halfway between sustainable and survival levels. 77% of those revenues were from delivery and 23% of them were through training and coaching. Our relationships which had begun sliding in Q1 stabilized. Betsy and Jennifer were working closely together, which was great; but I was &lt;a href="https://www.youtube.com/watch?v=NGrLb6W5YOM"&gt;off all by myself&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We were excited that our revenues were slowly being decoupled from typing code or otherwise billing dollars for hours, but we still struggled to market and do business development for our own products.&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 3 - Q3 2018 - Will Ship for Revenue Share?
&lt;/h4&gt;

&lt;p&gt;In Q2, one of our &lt;a href="https://www.wecohere.com/products/technical-coaching/?utm_campaign=cohere-2018-year-in-review&amp;amp;utm_content=xp3-long-time-tech-coaching-client"&gt;long-time technical coaching clients&lt;/a&gt; had a problem to solve. One of the products they've had for decades felt like it could be &lt;a href="https://en.wikipedia.org/wiki/Blue_Ocean_Strategy"&gt;a blue ocean&lt;/a&gt; in their market niche, but they lacked the funds to invest in integrating it with their main product line.&lt;/p&gt;

&lt;p&gt;Over a few months of discussions, we decided to pull the trigger on a deal where we matched their investment dollar for dollar in exchange for a 50% revenue share from the product line we were extending.&lt;/p&gt;

&lt;p&gt;Jennifer scaled back her coaching and training marketing and business development efforts and took point on delivering the product with Betsy. The entire project has taken us six months of quarter-time investment, and our partners are demoing it and beginning to transition clients to it in Q1 2019.&lt;/p&gt;

&lt;p&gt;At the end of Q3 2018, our revenues had rebounded to slightly below sustainable levels; with 26% of those revenues from the &lt;a href="https://avdi.codes/moom/?utm_campaign=cohere-2018-year-in-review"&gt;Mastering Object Oriented Programming&lt;/a&gt; course, 15% from training and coaching, and 59% from typing code for money. Our relationships were recovering, and my physical and mental health had begun to stabilize.&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 4 - Q4 2018 - Reaching a Broader Market
&lt;/h4&gt;

&lt;p&gt;We stepped into Q4 feeling pretty good. Our non-delivery revenues were trending in the right direction. Our first product launch was a success. There was demand for the services we were offering &lt;em&gt;beyond&lt;/em&gt; typing code for money.&lt;/p&gt;

&lt;p&gt;But there was another trap we had to navigate. Our customers are predominantly people who &lt;em&gt;already knew us.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This isn't a bad thing! It's given us an &lt;em&gt;almost&lt;/em&gt; sustainable livelihood in 2018. But we're not entirely sure people are buying our services because they &lt;em&gt;value the services&lt;/em&gt; or because they &lt;em&gt;value us.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is not a complaint. It's a beautiful feeling to be so well-liked that someone will carve out a few grand from their training budget to work with you. In fact, I'm bragging a little bit and reveling in the feeling. Mmmmm.&lt;/p&gt;

&lt;p&gt;But from a long-term business viability perspective, it kept us up at night. So we invested Q4 in building out a more intentional practice of marketing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it failed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We spent a lot of time on self-promotion instead of &lt;em&gt;meeting people where they are&lt;/em&gt;. Yes, we're super excited about helping people have more delightful pair-programming experiences and we successfully partnered to &lt;a href="https://www.kickstarter.com/projects/marlenac/lets-pair?utm_campaign=cohere-2018-year-in-review"&gt;kickstart a zine series on the topic&lt;/a&gt; but we were spending a lot of the trust we'd earned in the community to do so.&lt;/p&gt;

&lt;p&gt;We did not like that our marketing efforts were spending trust instead of building it. We went back to the drawing board and hired &lt;a href="https://docs.google.com/document/d/1FK3wqpC1V156KBiZ_UfA7lhu6atpYBkuCvqGJB20fiA/edit?usp=drive_web&amp;amp;ouid=103291621646598302066"&gt;a marketing professional.&lt;/a&gt; Further, we shifted our marketing efforts from convincing people to buy our shit, to sharing what we were thinking, learning and doing on &lt;a href="https://dev.to/cohere?utm_campaign=cohere-2018-year-in-review"&gt;dev.to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Jennifer's work in Q1 and Q2 on marketing and business development for our coaching and training services paid off in Q4, capturing 60% of our revenue. Product and revenue share revenue dipped to 5%, and our typing code for money revenue dropped to 35% of our revenues.&lt;/p&gt;

&lt;p&gt;My physical and mental health is mostly recovered to a reasonable point. I've reduced my drinking to a drink (or three) one or two days a week. I'm spending my free time doing fun things (like running a &lt;a href="https://loog.games/?utm_campaign=cohere-2018-year-in-review"&gt;very casual gaming guild&lt;/a&gt;!) and re-investing in the friendships I'd neglected since 2015.&lt;/p&gt;

&lt;h4&gt;
  
  
  TL/DR - What We Learned in 2018
&lt;/h4&gt;

&lt;p&gt;In 2018 we learned what kinds of work we didn't enjoy doing. And perhaps just as importantly what kind of work we enjoy that we may even be able to make a living doing. The TL/DR?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  We &lt;em&gt;adore&lt;/em&gt; helping people solve their most interesting and painful programming and engineering management challenges.&lt;/li&gt;
&lt;li&gt;  Businesses are pretty willing to pay part-time people to solve a pressing need; especially if it's something that they think is valuable but can't justify the opportunity cost to take the time to do it internally.&lt;/li&gt;
&lt;li&gt;  Our most effective sales and marketing efforts are giving away free help when we can.&lt;/li&gt;
&lt;li&gt;  Coaching and training for teams and individuals on nuanced topics such as refactoring, testing, pair programming, hiring, interviewing, management etc. is &lt;em&gt;tough to sell&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;  Our favorite way to type code is maintaining software with a demonstrably viable business model. We love gently iterating it towards something its customers find more useful and its stakeholders find more profitable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a financial perspective, a picture paints a thousand words. The following graph shows our gross revenue, broken down into net revenues and across revenue sharing, product sales, training and coaching, and typing code for money.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aRiOLX_---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ifkmy3xr09hlyphl6xjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aRiOLX_---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ifkmy3xr09hlyphl6xjt.png" alt="Cohere's 2018 Year in Review financial graph." title="Cohere's 2018 year in Review"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don't know what 2019 holds, but one of the things we're planning to do is shift even further toward technology education, training, and coaching. We probably won't be sharing this much detail too often, but If you'd like to learn a bit more about engineering leadership from us every month &lt;a href="https://www.wecohere.com/newsletter?utm_campaign=cohere-2018-year-in-review"&gt;you can sign up for our newsletter&lt;/a&gt;, or you can follow &lt;a href="https://twitter.com/jtu"&gt;Jennifer&lt;/a&gt;, &lt;a href="https://twitter.com/betsythemuffin"&gt;Betsy&lt;/a&gt;, &lt;a href="https://twitter.com/zspencer"&gt;Zee&lt;/a&gt;, or &lt;a href="https://twitter.com/wecohere"&gt;Cohere&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>business</category>
      <category>career</category>
      <category>management</category>
      <category>yearinreview</category>
    </item>
    <item>
      <title>Watch Us Debug a Race Condition in a Brittle Cypress Test Live on January 3rd, 2019!</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Thu, 27 Dec 2018 22:19:28 +0000</pubDate>
      <link>https://dev.to/cohere/watch-us-debug-a-race-condition-in-a-brittle-cypress-test-live-on-january-3rd-2019-7ph</link>
      <guid>https://dev.to/cohere/watch-us-debug-a-race-condition-in-a-brittle-cypress-test-live-on-january-3rd-2019-7ph</guid>
      <description>&lt;p&gt;A few weeks ago, we asked the &lt;a href="https://dev.to/cohere/what-rails-or-js-programming-problems-do-you-wish-you-could-watch-someone-work-out-51pl"&gt;dev.to&lt;/a&gt; and broader programming community &lt;a href="https://twitter.com/zspencer/status/1074724243235950592"&gt;what programming problems they would like watch being solved on stream in a real-world context&lt;/a&gt;. ~40% of you wanted to see us solve a &lt;em&gt;race condition&lt;/em&gt; and another 30% wanted to see us solve a &lt;em&gt;brittle JavaScript feature test&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Seeing as Betsy and I are both "yes, and" kind of people (and there happened to be a really good representation of this in one of our &lt;a href="https://dev.to/products/technical-coaching/?utm_campaign=debugging-race-condition-livestream&amp;amp;utm_content=announce-link"&gt;technical coaching clients&lt;/a&gt; codebases) we figured we could show a race condition in a brittle JavaScript feature test!&lt;/p&gt;

&lt;p&gt;If you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Struggled to figure out when, exactly, test data setup is being completed&lt;/li&gt;
&lt;li&gt;  Been baffled by an &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt; value while chaining Promises&lt;/li&gt;
&lt;li&gt;  Deleted an end-to-end, browser feature test because "I can show the feature working when I do it, but the browser test is failing!"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then &lt;a href="https://wecohere.com/events/january-3rd-2019-livestream-of-debugging-a-race-condition-in-a-cypress-browser-test/?utm_campaign=debugging-race-condition-livestream&amp;amp;utm_content=main-cta"&gt;RSVP to attend our free live-stream on January 3rd from 10 AM PT/1PM ET to 11 AM PT/2PM ET where we will step through diagnosing, debugging, and resolving a race condition in a brittle cypress feature test&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;We're both pretty new to the streaming thing and want to limit our exposure to embarrassment; so &lt;a href="https://dev.to/events/january-3rd-2019-livestream-of-debugging-a-race-condition-in-a-cypress-browser-test/?utm_campaign=debugging-race-condition-livestream&amp;amp;utm_content=requiring-rsvp-attend-link"&gt;we're requiring people to RSVP&lt;/a&gt;. Attendees will be sent an email a few minutes before the stream starts with a link to join.&lt;/p&gt;

&lt;p&gt;We're hoping to record the screen, audio, and video stream for later, but… again.. We're new to streaming video. So no promises.&lt;/p&gt;

&lt;p&gt;We'll be monitoring the &lt;a href="https://twitter.com/search?f=tweets&amp;amp;q=%23realworldcode"&gt;#RealWorldCode&lt;/a&gt; and &lt;a href="https://twitter.com/search?f=tweets&amp;amp;q=%23UntanglingJS"&gt;#UntanglingJS&lt;/a&gt; hashtags on Twitter before, throughout and after the stream and would love to hear your questions, thoughts, and insights!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://www.wecohere.com/articles/debugging-a-race-condition-live-on-stream-on-january-3rd.html?utm_campaign=debugging-race-condition-livestream&amp;amp;utm_content=attribution-link"&gt;wecohere.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>testing</category>
      <category>cypress</category>
    </item>
    <item>
      <title>Understanding Git: Configuration of Repositories and Remotes</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Tue, 18 Dec 2018 22:06:48 +0000</pubDate>
      <link>https://dev.to/cohere/understanding-git-configuration-of-repositories-and-remotes-39g8</link>
      <guid>https://dev.to/cohere/understanding-git-configuration-of-repositories-and-remotes-39g8</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published by Cohere, we have a number of other &lt;a href="https://www.wecohere.com/topics/engineering/?utm_campaign=understanding-git-configuration-repositories-and-remotes&amp;amp;utm_content=header"&gt;articles on Engineering on our website.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently, a &lt;a href="https://dev.to/jessachandler/explain-git-config-like-im-five-750"&gt;dev.to community member asked if someone could explain&lt;code&gt;.git/config&lt;/code&gt; like they are five&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I first started attempting to help, I  realized that to explain &lt;code&gt;.git/config&lt;/code&gt;, we need to start a few steps back. First, by looking at the history of software configuration; then investigating the differences between a &lt;em&gt;remote&lt;/em&gt; and a &lt;em&gt;repository&lt;/em&gt; is in Git. and finally showing how &lt;code&gt;.git/config&lt;/code&gt; pulls them together in a powerful tool that decreases your cognitive load when shoveling code back and forth on the Internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.quora.com/Why-is-Git-so-hard-to-learn"&gt;Git is difficult by design&lt;/a&gt;&lt;em&gt;.&lt;/em&gt; That's why I struggle with Git. If you also struggle with Git, that might be why too. Git's deliberate difficulty isn't necessarily a bad thing. But it does mean that becoming fluent in Git requires us to invest in building a deep and accurate mental model.&lt;/p&gt;

&lt;p&gt;But first, let's look at how the current model for Unix program configuration came to be. This will let us diagnose unusual behaviors in any application that's configured with dotfiles, not just Git! If you're already familiar with user and directory level dotfiles, you may want to skip ahead to Configuring Git. For those who love history and nuance, read on!&lt;/p&gt;

&lt;h3&gt;
  
  
  A (Sharply Abridged) History of Software Configuration
&lt;/h3&gt;

&lt;p&gt;Once upon a time, we operated computers by writing a program's instructions directly in binary on &lt;a href="https://en.wikipedia.org/wiki/Punched_card"&gt;punch cards&lt;/a&gt;. The program was executed card-by-card until it was complete, at which point the output was printed onto paper.&lt;/p&gt;

&lt;p&gt;This meant changing the behavior of a program required &lt;em&gt;physical changes&lt;/em&gt; to the cards. Either by injecting new cards or replacing them. There weren't any programs that changed their behavior depending on &lt;em&gt;where&lt;/em&gt; or &lt;em&gt;by whom&lt;/em&gt; the program was run.&lt;/p&gt;

&lt;p&gt;Over time, computers became more robust and the mechanisms for inputting and distributing programs less precarious. More and more people adopted them. And people have &lt;em&gt;preferences&lt;/em&gt;. Programs needed a way to know the &lt;em&gt;default behavior&lt;/em&gt; the person running it wanted.&lt;/p&gt;

&lt;p&gt;So programmers instructed their programs to look for a file specific to the person using the program, load the data stored in that file, and reference it when making decisions about how to behave.&lt;/p&gt;

&lt;p&gt;These configuration files are often readable by both humans and computers. This lets both robots and people perform diagnostics and adjustments.&lt;/p&gt;

&lt;p&gt;This worked pretty well! People could decide if they want the program to &lt;em&gt;bleep&lt;/em&gt; when done instead of &lt;em&gt;blorp&lt;/em&gt;, or otherwise adjust details the programmer took the time to expose as configurable.&lt;/p&gt;

&lt;p&gt;As programs became more general purpose, the &lt;em&gt;working directory&lt;/em&gt; (the location on the filesystem where the person is running the program) became relevant in determining the program's behavior.&lt;/p&gt;

&lt;p&gt;People don't want to have to pass in &lt;code&gt;--group-by spending_category&lt;/code&gt; when we run a &lt;code&gt;summarize_csv&lt;/code&gt; program in my &lt;code&gt;business-finance/&lt;/code&gt; directory! That's a whole extra 26 keystrokes! We want to run &lt;code&gt;summarize_csv&lt;/code&gt;, and have the computer understand the rest automatically.&lt;/p&gt;

&lt;p&gt;So programmers instructed their program to &lt;em&gt;also&lt;/em&gt; look for a configuration file within the current working directory. As personal or project level needs became more complex, these individual files evolved into folders to store the myriad details necessary to meet these needs.&lt;/p&gt;

&lt;p&gt;So… what does this have to do with Git?&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Git
&lt;/h3&gt;

&lt;p&gt;Git's purpose is to keep track of changes to files within a particular directory and to allow one to distribute those changes to other git users. It relies on the &lt;code&gt;.git&lt;/code&gt; folder within that directory to store both those changes and how it tracks them. The  &lt;code&gt;.git/config&lt;/code&gt; file in that directory exists to provide default or contextual behavior to the &lt;code&gt;git&lt;/code&gt; program. If you poke around inside the &lt;code&gt;.git&lt;/code&gt; folder in a project, you'll see all kinds of files and directories that map back to concepts in Git.&lt;/p&gt;

&lt;p&gt;When distributing changes, there are two sides to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Where may I find changes that I want to apply?&lt;/li&gt;
&lt;li&gt;  Where may I send changes so that others may apply them?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before Github, common practice was for each person kept a local copy of the folders history on their machine as a &lt;em&gt;local repository&lt;/em&gt;. They would then grant access to it via HTTP, SSH or email.&lt;/p&gt;

&lt;p&gt;This required the repository owner to maintain a server that others could access as a &lt;em&gt;remote repository&lt;/em&gt;. They'd also need to do the work to ensure the repository remained accessible to those who wanted it and manage who is allowed to write to the repository.&lt;/p&gt;

&lt;p&gt;Developers with write permissions make changes &lt;em&gt;locally&lt;/em&gt; and &lt;em&gt;push&lt;/em&gt; them to their publicly readable &lt;em&gt;remote&lt;/em&gt; server.&lt;/p&gt;

&lt;p&gt;Github removed the requirement to manage your own infrastructure when you want other people to be able to read or contribute to your code. Github absorbed the hosting and maintenance costs of the &lt;em&gt;remote&lt;/em&gt; and provided powerful tools for offering changes to the project maintainer.  While people have been forking (creating a copy of a project so you can customize it) open source projects for decades, Github made it drop-dead easy to fork the original maintainer's &lt;em&gt;remote repository&lt;/em&gt; from the original maintainer to a &lt;em&gt;remote&lt;/em&gt; repository you own. You can then &lt;em&gt;clone&lt;/em&gt; your copy down and push whatever changes to it you like; then suggest to the maintainer that they apply your changes using a &lt;em&gt;pull request&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you're the maintainer or person contributing a change there are now 4 repositories in play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The maintainer's local repository&lt;/li&gt;
&lt;li&gt;  The contributor's local repository&lt;/li&gt;
&lt;li&gt;  The maintainers GitHub remote that mirrors their local repository&lt;/li&gt;
&lt;li&gt;  The contributors GitHub remote that mirrors the maintainers GitHub remote&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each repository is accessible via its own unique URL. You may notice you don't have to type that URL each and every time you &lt;em&gt;push&lt;/em&gt; to or &lt;em&gt;pull&lt;/em&gt; from Github. That's because the &lt;code&gt;git&lt;/code&gt; command line program stores this configuration information in the &lt;code&gt;.git/config&lt;/code&gt; file. When you crack that file open, you see something like the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true

[remote "origin"]
    url = git@github.com:your-git-username/your-project.git
    fetch = +refs/heads/*:refs/remotes/origin/*

[branch "master"]
    remote = origin
    merge = refs/heads/origin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The syntax of this file mirrors that of the &lt;a href="https://en.wikipedia.org/wiki/INI_file"&gt;INI file format&lt;/a&gt;, which is a human and machine readable format that uses square brackets to group configuration options and key-value pairs that use a single equals sign to assign values to the configuration.&lt;/p&gt;

&lt;p&gt;If you want to learn more about what options are available to configure in the &lt;code&gt;.git/config&lt;/code&gt; option, you can run the &lt;code&gt;man&lt;/code&gt; program from your terminal like so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;man git-config&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This opens the (extensive!) manual for &lt;code&gt;git-config&lt;/code&gt; and allows you to use the &lt;code&gt;/&lt;/code&gt; key to enter "search" mode or use the up and down arrows and page up/page down keys to browse through and search for options. There's a lot of options to wade through! &lt;a href="https://dev.to/zspencer/comment/6m9n"&gt;If you want more detail on the "remote" and "branch" configuration groupings, you can read my original reply on dev.to&lt;/a&gt;.&lt;a href="https://dev.to/zspencer/comment/6m9n"&gt; &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those of us still working our way towards a strong mental model of Git, keep strong! You can do it! But keep in mind that it's OK to not know everything about the tool. All you need is a level of fluency that meets &lt;em&gt;your own needs and desires&lt;/em&gt;. You don't need to know all the things just because the technocratic gatekeepers in our industry think you "should."&lt;/p&gt;




&lt;p&gt;If you found this post helpful, we'd love it if you subscribed to our &lt;a href="https://www.wecohere.com/newsletter?utm_campaign=understanding-git-configuration-repositories-and-remotes&amp;amp;utm_content=footer"&gt;monthly newsletter&lt;/a&gt;! We share some of the things we've found valuable each month, as well as exclusive insights into being a more effective programmer and engineering leader.&lt;/p&gt;

</description>
      <category>git</category>
      <category>productivity</category>
      <category>linux</category>
      <category>github</category>
    </item>
    <item>
      <title>What Rails or JS programming problem(s) do you wish you could watch someone work out?</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Mon, 17 Dec 2018 18:14:20 +0000</pubDate>
      <link>https://dev.to/cohere/what-rails-or-js-programming-problems-do-you-wish-you-could-watch-someone-work-out-51pl</link>
      <guid>https://dev.to/cohere/what-rails-or-js-programming-problems-do-you-wish-you-could-watch-someone-work-out-51pl</guid>
      <description>&lt;p&gt;Hello Friends!&lt;/p&gt;

&lt;p&gt;In 2019, we're shifting our focus away from client work and towards accessible programming education. &lt;/p&gt;

&lt;p&gt;We're in a lucky position where we have some real-world, open-or-near-open source production applications that are giving us a lot of nuanced problems to work through. And we're thinking "Why not take the time to package some of that up into something that's useful for people that aren't us?"&lt;/p&gt;

&lt;p&gt;If you could ask two people with ~30-ish combined years of experience writing production web applications to walk you through step-by-step how to solve a particular problem in video form what would it be?&lt;/p&gt;

&lt;p&gt;Would it be adding a feature to an existing code base over a few hours? Diagnosing and resolving performance issues? Performing a hairy, multi-step refactor?&lt;/p&gt;

&lt;p&gt;Maybe something completely different we'd never thought of? Whatever it is, we'd love to hear about it so we can figure out a way to show how we would approach the problem together. Also, if you want you can vote in &lt;a href="https://twitter.com/zspencer/status/1074724243235950592"&gt;an informal twitter poll&lt;/a&gt;, in which we regret not providing an option for CodeyMcCodeFace.&lt;/p&gt;

&lt;p&gt;Zee &amp;amp; Betsy&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>learning</category>
      <category>rails</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Engineering Managers: It's Time to Write Better Job Descriptions</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Mon, 10 Dec 2018 19:59:13 +0000</pubDate>
      <link>https://dev.to/cohere/engineering-managers-its-time-to-write-better-job-descriptions-1c26</link>
      <guid>https://dev.to/cohere/engineering-managers-its-time-to-write-better-job-descriptions-1c26</guid>
      <description>&lt;p&gt;&lt;a href="https://www.techrepublic.com/article/software-had-the-highest-job-turnover-rate-of-any-industry-in-2017/"&gt;Software engineers have the highest turnover rate of any industry&lt;/a&gt; with an &lt;a href="http://blog.indeed.com/2017/04/03/silicon-valley-tech-job-migration/"&gt;average tenure of 2~3 years&lt;/a&gt;. Why? Because &lt;em&gt;&lt;a href="https://www.forbes.com/sites/victorlipman/2015/08/04/people-leave-managers-not-companies/#a564b7347a94"&gt;people leave managers, not companies&lt;/a&gt;&lt;/em&gt;, and as Nicole Sanchez and Danilo Campos point out, &lt;a href="https://twitter.com/nmsanchez/status/935654372276453376"&gt;tech is full of poor managers&lt;/a&gt;. I've been one of them!&lt;/p&gt;

&lt;p&gt;Part of what makes a poor manager is a lack of investment into the day-to-day activities of management and an over-emphasis on the day-to-day activities of a practitioner. One of these day-to-day management activities is understanding the work that is being done, why it's being done, and how it's being done. Because this is a constantly adapting answer, it's important to spend time communicating these things to potential new-hires and existing team members. How else can we expect others to understand what is expected of them? Or what a growth trajectory could look like within your team or broader organization? One way to communicate this is to &lt;em&gt;write job descriptions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's very very tempting to rely on &lt;em&gt;job titles&lt;/em&gt; as a proxy for a job description. But when we rely mostly on job titles we are implicitly considering three things: the skills we're looking for, the level those skills are at, and what behaviors we want to encourage or discourage. This reduction collapses context and we wind up posting think-pieces about "&lt;em&gt;what does 'senior' even mean anyway?"&lt;/em&gt; or twitter rants declaring "&lt;em&gt;devops is distinct from site reliability engineering!&lt;/em&gt;". That communication to the future hire or to the team often does not occur.&lt;/p&gt;

&lt;p&gt;To communicate the necessary skills and levels and behaviors you seek, write a job description with clearly written expectations about what skills at which level are necessary for the given job titles activities within &lt;em&gt;your team or departments context&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;"Ugh" you grunt. "I read my job description, the most important phrase was 'other duties as assigned!'" And you're right. A lot of job descriptions are miserable. They're written under duress by overloaded engineering managers trying to satisfy HR while simultaneously navigating the difficulties of leading an engineering team towards accomplishing the organization's goals.&lt;/p&gt;

&lt;p&gt;As a result, job descriptions are often a list of buzzwords tied to the technology stack with years of experience assigned. It's the simplest path towards a somewhat valid job description. Plus it allows the recruiting team to write job posts and perform pass/fail analysis of candidate resumes or brief phone calls.&lt;/p&gt;

&lt;p&gt;We can do better, and our current and future employees and team mates &lt;em&gt;deserve better&lt;/em&gt;. They deserve &lt;strong&gt;&lt;em&gt;a functional understanding of what is expected from them for the day to day and what they can anticipate as a reward for growing their skills.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So how do we write useful job descriptions? Ones that accurately convey expectations and career trajectory in a meaningful way to team members?&lt;/p&gt;

&lt;h2&gt;
  
  
  Write Better Job Descriptions
&lt;/h2&gt;

&lt;p&gt;When writing job descriptions, &lt;strong&gt;&lt;em&gt;explicit is better than implicit&lt;/em&gt;&lt;/strong&gt;. Describe the behavior, activity, or skill that you use day-to-day. . Don't rely on "computer science fundamentals" to convey "Transforms data from event streams into a reportable format."&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;&lt;em&gt;use language that is growth oriented, not credential oriented&lt;/em&gt;&lt;/strong&gt;. Sure, you can use "computer science degree or relevant experience" as shorthand, but isn't what you really want "has delivered features and bug fixes to production web applications"?&lt;/p&gt;

&lt;p&gt;Third, &lt;strong&gt;&lt;em&gt;avoid value judgements about the person&lt;/em&gt;&lt;/strong&gt;. "Good engineers perform code review in a timely manner without being an ass," while laudable, places too much emphasis on the &lt;strong&gt;actor&lt;/strong&gt; and not enough on &lt;strong&gt;the activity.&lt;/strong&gt; Instead, "Performs code reviews by providing specific, actionable and compassionate feedback in a timely manner."&lt;/p&gt;

&lt;p&gt;Fourth, &lt;strong&gt;&lt;em&gt;remove anything that is extraneous.&lt;/em&gt;&lt;/strong&gt; Attempting to put every last detail of what it means to be a senior developer on your team into your job description can wind up encouraging investment in minor activities or skills that are nice to have but not core. Instead, leave enough white-space that your team members can fill in the margins with the unique and beautiful ways they build up your team. These unique skills may even lead to writing a new job description if they wind up being incredibly worthwhile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Describing Behaviors and Activities
&lt;/h3&gt;

&lt;p&gt;When describing activities for your job description, focus on the activities that people with the role spend a significant amount of time on. If you expect your engineers to invest in test automation, for instance, you would include a behavior of "writing automated tests that validate the use-cases for features under the team's purview."&lt;/p&gt;

&lt;p&gt;Further, It is useful to include &lt;em&gt;why&lt;/em&gt; the activity is performed or the behavior is encouraged. While "Delivers features and bug fixes at the direction of the business and product team" is a decent start for developers and may even be acceptable for an engineer in their first or second job it fails to encourage developers to understand and act in line with the motivation for the features. This results in a "I do as I'm told" mentality. Instead, consider "Makes discernable progress towards business objectives by delivering features and bug fixes aligned with the direction of the product and business team."&lt;/p&gt;

&lt;p&gt;We've made the explicit additional responsibility that the work that is being done &lt;em&gt;drives tangible progress.&lt;/em&gt; Now a team member has a better idea of how they'll be assessed and how to adjust their behavior to support that assessment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Describing Skills
&lt;/h3&gt;

&lt;p&gt;Skills are even harder to describe objectively than behaviors and activities. This is where most job descriptions and job postings fall over, and how you get job listings asking for &lt;a href="https://twitter.com/kevinkaywho/status/898622008019124224"&gt;8+ years swift experience&lt;/a&gt;, I encourage the use of a &lt;em&gt;fluency model&lt;/em&gt; to describe skills.&lt;/p&gt;

&lt;p&gt;Fluency models focus on &lt;em&gt;the level of mental effort required to leverage a particular skill.&lt;/em&gt; Technology organizations greatest costs are &lt;em&gt;time,&lt;/em&gt; &lt;em&gt;thought&lt;/em&gt; and &lt;em&gt;effort.&lt;/em&gt; Making explicit the relationship between time, thought and effort and the skills you are cultivating brings the focus towards what the organization gets out of higher-skilled workers.&lt;/p&gt;

&lt;p&gt;Fluency has less to do with the &lt;em&gt;person&lt;/em&gt; and more to do with their &lt;em&gt;abilities&lt;/em&gt;. This helps us more effectively decouple value judgements, i.e. "Great React Developer." Instead, we can be more specific and say that an associate front end developer "requires active support to design and implement React components with data or event inter-dependencies." It avoids judging the person and sets an expectation that support will be provided. For more senior team members, a corresponding "supports other team members in designing and implementing components with complex data or event inter-dependencies" may be appropriate; and will also signal to your associate level team members that one of the ways they can advance their career is by supporting those around them.&lt;/p&gt;

&lt;p&gt;Most people are familiar with fluency as a concept. When we ask questions about fluency we give people the opportunity to communicate in stories. Stories give a higher-fidelity understanding of the person's actual skills. The question "What was the last challenge you struggled with when implementing a feature in React?" is both much easier to ask and answer and gives a much richer field for follow-on questions to dive in and develop a more nuanced view (or detect possible bullshit).&lt;/p&gt;

&lt;h2&gt;
  
  
  Now Write A Job Description!
&lt;/h2&gt;

&lt;p&gt;Take 30 minutes right now to write a job description. What are the day-to-day activities you perform in your role? What are the skills you use most frequently? Where are the edges of fluency between your role and more or less senior ones?&lt;/p&gt;

&lt;p&gt;Next, post it up as a comment on this post on dev.to! I'll do my best to read each and every one and give specific, actionable, and compassionate feedback.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you like this, &lt;a href="https://www.wecohere.com/newsletter?utm_campaign=write-better-job-descriptions&amp;amp;utm_content=post-footer"&gt;we offer a monthly engineering leadership newsletter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Our newsletter gives you a never-before-seen, fresh-from-our desks article about engineering leadership or programming, as well as summaries and links to other articles or podcasts we found informative.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Plus, it lets you know what kind of public training we're offering over the next few months, often with a pretty decent discount!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>management</category>
      <category>leadership</category>
      <category>culture</category>
      <category>career</category>
    </item>
    <item>
      <title>Implementation Strategies for Integrating External Services Into Your Application</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Thu, 29 Nov 2018 22:28:40 +0000</pubDate>
      <link>https://dev.to/cohere/implementation-strategies-for-integrating-external-services-into-your-application-30kh</link>
      <guid>https://dev.to/cohere/implementation-strategies-for-integrating-external-services-into-your-application-30kh</guid>
      <description>&lt;p&gt;Much of modern software relies on external services to take responsibility for some aspect of the product's feature set. This trend will only continue as mostly-single-purpose, non-database, API-first services such as &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt;, &lt;a href="https://www.drip.com/" rel="noopener noreferrer"&gt;Drip&lt;/a&gt;, &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;, &lt;a href="https://www.nylas.com/" rel="noopener noreferrer"&gt;Nylas&lt;/a&gt;, and &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; continue to evolve and come to market.&lt;/p&gt;

&lt;p&gt;This is a good thing! It provides developers with leverage so we can focus on solving the customer's pain-points instead of special-purpose, very hard problems over and over again.&lt;/p&gt;

&lt;p&gt;However, adopting an external service has costs. There are (broadly speaking) three ways developers tend to integrate with these external services, each with different pros and cons. Here they are ranked from 'simplest' to 'most complex':&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Directly embedding calls to the service or its SDK into existing application code. In Rails, this may look like calling the service directly from your models or controllers. In React or Vue, you may call these services directly from the components.&lt;/li&gt;
&lt;li&gt;  Wrapping the service or its provided SDK in a custom library, and calling that library from within your application code.&lt;/li&gt;
&lt;li&gt;  Writing an internal service (Micro or otherwise) that wraps the external service or its SDK and an internal library for calling that service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these options are entirely valid. Different organizational contexts, team dynamics, and technical affordances and constraints fit different solutions better. I ask myself a number of questions to figure out which tactic to apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Am I trying to learn how the service works and apply it in a proof of concept or am I shipping a production feature?&lt;/li&gt;
&lt;li&gt;How closely does the service's or SDK's interface align with the conventions and shared understandings that are baked into the existing codebase? Cognitive load is increased when the affordances of a library conflict with the norms established within the codebase.&lt;/li&gt;
&lt;li&gt;Am I interacting with the service in a variety of locations in the codebase? Are there other internal consumers of the service?&lt;/li&gt;
&lt;li&gt;Am I testing using grey or black box strategies that don't have access the the processes memory? Or am I testing in a way affords convenient in-process test-doubles or fakes?&lt;/li&gt;
&lt;li&gt;What is the likelihood that the service or its SDK will change its interface in a way that requires us to change our code?&lt;/li&gt;
&lt;li&gt;How do I intend to handle the service failing? Is it possible to recover without blocking the end-user from using the feature, or am I going to have to tell the user to try again later?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Simplest Case: Embedding Calls to an External Service Directly
&lt;/h3&gt;

&lt;p&gt;If this is the first implementation of a feature that relies on the service and I can comfortably encapsulate responsibility for handling the service failure modes within the ruby class or node module I'll go with the simplest option: writing the service calls directly into the existing application code.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Getting Stronger: Encapsulating External Service with a Wrapper Library
&lt;/h3&gt;

&lt;p&gt;Once I'm hitting more complex failure cases, using the service across a variety of features, or experiencing friction due to conflicts between the affordances the library affords and the norms established within the codebase I tend to &lt;a href="https://agilewarrior.wordpress.com/2010/11/19/refactoring-legacy-code-sprout-method/" rel="noopener noreferrer"&gt;sprout&lt;/a&gt; an internal library that wraps the external SDK. The internal library starts as a very small class with a method that calls the SDK, or if there are several methods and the interface seems good enough I'll apply the delegator pattern using &lt;a href="https://ruby-doc.org/stdlib-2.5.1/libdoc/forwardable/rdoc/Forwardable.html" rel="noopener noreferrer"&gt;Forwardable&lt;/a&gt; or &lt;a href="https://guides.rubyonrails.org/active_support_core_extensions.html#method-delegation" rel="noopener noreferrer"&gt;ActiveSupport's &lt;code&gt;delegate&lt;/code&gt; core extension&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Advanced Hardening: Encapsulating the External Service with an Internal Service
&lt;/h3&gt;

&lt;p&gt;If the application deployment and configuration ecosystem is already geared towards inter-service communication, and there are other internal services that consume the external service I may take it a step further and sprout an internal service that encapsulates the external service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping it Together
&lt;/h3&gt;

&lt;p&gt;The end goal in these cases is not to make a canonical, brilliant adapter that perfectly encapsulates the service. Instead, it's to help future-me and my teammates by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Leaning into our existing conventions and designs.&lt;/li&gt;
&lt;li&gt;  Providing a seam that allows us to inject behavior or change naming in order to more accurately reflect what we're using the library for.&lt;/li&gt;
&lt;li&gt;  Provide an initial pattern for us to follow (or adapt) as we implement new features with the service.&lt;/li&gt;
&lt;li&gt;  Ensures that if the underlying API changes, we can update the usages in one place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;External services are powerful tools and can save many thousands of dollars in engineering time. Investing in integrating them in an easier-to-maintain and adapt manner will pay dividends within months. Next time you're about to add in a new service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Start with the cheapest and easiest solution: embedding the external service directly into your existing classes and modules.&lt;/li&gt;
&lt;li&gt;  As complexity increases, extract a new class or module which encapsulates the responsibility in a manner that fits with your existing patterns.&lt;/li&gt;
&lt;li&gt;  If you're on the services bandwagon, consider extracting an internal service once it becomes useful; Say when you have a few different teams and their services using the same external service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For further reading, I'd start with Alistair Cockburn and Michael Feathers &lt;a href="http://wiki.c2.com/?HexagonalArchitecture" rel="noopener noreferrer"&gt;article on the c2 wiki on Ports and Adapters (aka Hexagonal Architecture)&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you liked this, consider signing up for &lt;a href="https://www.wecohere.com/newsletter?utm_campaign=ports-and-adapters-external-services-post&amp;amp;utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_content=footer" rel="noopener noreferrer"&gt;free engineering leadership newsletter&lt;/a&gt;. We offer monthly insights, both technical and social that help you be a more effective engineering leader.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>node</category>
      <category>architecture</category>
      <category>refactoring</category>
    </item>
  </channel>
</rss>
