<?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: Cohere</title>
    <description>The latest articles on DEV Community by Cohere (@cohere).</description>
    <link>https://dev.to/cohere</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%2Forganization%2Fprofile_image%2F372%2F562ef468-9b35-427a-bb83-974d34adbe57.png</url>
      <title>DEV Community: Cohere</title>
      <link>https://dev.to/cohere</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cohere"/>
    <language>en</language>
    <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>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>Haskell, Vectors, and Implicit Knowledge</title>
      <dc:creator>Betsy Haibel</dc:creator>
      <pubDate>Thu, 03 Jan 2019 16:01:36 +0000</pubDate>
      <link>https://dev.to/cohere/haskell-vectors-and-implicit-knowledge-4bal</link>
      <guid>https://dev.to/cohere/haskell-vectors-and-implicit-knowledge-4bal</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Hey folks!&lt;/p&gt;

&lt;p&gt;This piece is a few years old now, but I'm republishing it on dev.to as I move more of my stuff here. It's kind of personal. Unlike a lot of my other work, it's not about teaching folks how to tech better -- it's about why it's so important to me to do so in an accessible way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This winter I was teaching myself Haskell.&lt;/p&gt;

&lt;p&gt;I'd tried before, with everyone's darling &lt;em&gt;Learn You a Haskell for Great Good&lt;/em&gt;. My old boss had compared its "offbeat" approach to &lt;em&gt;_why's Poignant Guide to Ruby&lt;/em&gt;, which I love. Both have an informal tone, but the similarities stop there: where the Poignant Guide has adorable cartoon foxes, &lt;em&gt;Learn You a Haskell&lt;/em&gt; has upsetting bro humor. I think I snapped at one of the (many) fat jokes.&lt;/p&gt;

&lt;p&gt;This time around I tried &lt;em&gt;Real World Haskell&lt;/em&gt;, and at first things went much better. The material was more usefully organized, and the examples and exercises better suited to my learning style.&lt;br&gt;
Then I came to The Problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The problem set at the end of Real World Haskell’s third chapter contains the following sequence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Consider three two-dimensional points a, b, and c. If we look at the angle formed by the line segment from a to b and the line segment from b to c, it either turns left, turns right, or forms a straight line. Define a Direction data type that lets you represent these possibilities.&lt;/li&gt;
&lt;li&gt;Write a function that calculates the turn made by three 2D points and returns a Direction.&lt;/li&gt;
&lt;li&gt;Define a function that takes a list of 2D points and computes the direction of each successive triple. Given a list of points [a,b,c,d,e], it should begin by computing the turn made by [a,b,c], then the turn made by [b,c,d], then [c,d,e]. Your function should return a list of Direction.&lt;/li&gt;
&lt;li&gt;Using the code from the preceding three exercises, implement Graham's scan algorithm for the convex hull of a set of 2D points. You can find good description of what a convex hull is, and how the Graham scan algorithm should work, on Wikipedia.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Problem 9 was pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Straight&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Problem 10 took me a month.&lt;/p&gt;

&lt;p&gt;I figured it had to be simple; if it were a difficult geometry problem, it wouldn’t be in a beginners' Haskell book, it would be in a "Haskell as applied to tricky math" book. But approach after approach after approach failed me. I compared slopes. I tried complicated conditionals based on what quadrant each line was in when its base was set at origin.&lt;/p&gt;

&lt;p&gt;Did you know that Haskell has two &lt;code&gt;Data.Vector&lt;/code&gt; modules? There's one in the standard library that deals with Vectors like the array-like CS concept. There's one on Hackage that deals with Vectors like the geometry and second-year algebra concept. They are called the same thing. I know this now, because I found the docs for the latter, imported &lt;code&gt;Data.Vector&lt;/code&gt; like a good girl, and spent aeons of subjective time banging my head against arity mismatch errors before giving up on a dot-product-based solution.&lt;/p&gt;

&lt;p&gt;Eventually I finally hammered out all of the special cases.&lt;/p&gt;

&lt;p&gt;I’d been pushing myself to finish, rather than cutting my losses and moving on in the book, because I figured it would feel good when I finally did. It didn’t. I just felt stupid, because it had taken me a month to figure out what the authors of the book had clearly expected to be simple.&lt;/p&gt;

&lt;p&gt;I moved on to Problem 12. Looked up the Graham Scan algorithm on Wikipedia, as ordered. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Again, determining whether three points constitute a "left turn" or a "right turn" does not require computing the actual angle between the two line segments, and can actually be achieved with simple arithmetic only. For three points &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qy0-KPqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/e/3/8/e38e464b2ad39be72af38cacc8fc17de.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qy0-KPqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/e/3/8/e38e464b2ad39be72af38cacc8fc17de.png" alt="p1=(x1,y1)"&gt;&lt;/a&gt;, &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xz6tybA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/a/4/5/a45267c35535663e45d3a0bcc4db16e6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xz6tybA0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/a/4/5/a45267c35535663e45d3a0bcc4db16e6.png" alt="p1=(x2,y2)"&gt;&lt;/a&gt; and &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NU4RLRw4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/b/d/b/bdb6d1f831105c159498d203a95c41fb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NU4RLRw4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/b/d/b/bdb6d1f831105c159498d203a95c41fb.png" alt="p1=(x3,y3)"&gt;&lt;/a&gt;, simply compute the z-coordinate of the cross product of the two vectors &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1WjOoppD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/a/c/7/ac7a0fe21575792e379eb1caa37d5224.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1WjOoppD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/a/c/7/ac7a0fe21575792e379eb1caa37d5224.png" alt="p1p2"&gt;&lt;/a&gt; and &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5406PX2O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/2/b/5/2b5ba9e273a2f3c0de10b0a07125bd86.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5406PX2O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/2/b/5/2b5ba9e273a2f3c0de10b0a07125bd86.png" alt="p1p3"&gt;&lt;/a&gt;, which is given by the expression &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d0uL_Hwx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/4/4/d/44dac4ebbcdd18ed0593418004b5e4c8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d0uL_Hwx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/math/4/4/d/44dac4ebbcdd18ed0593418004b5e4c8.png" alt="(x2 - x1)(y3 - y1) - (y2 - y1)(x3 - x1)"&gt;&lt;/a&gt;. If the result is 0, the points are collinear; if it is positive, the three points constitute a "left turn" or counter-clockwise orientation, otherwise a "right turn" or clockwise orientation (for counter-clockwise numbered points).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Reader, I saw fire.&lt;/p&gt;

&lt;p&gt;At first I was angry at myself, because there had been a straightforward answer and '’d "just been too stupid" to get it. Then I was angry at the authors for not putting that "simple arithmetic" answer in the text for problem 10. Then I was furious at the authors — because I realized why they hadn't.&lt;/p&gt;

&lt;p&gt;They thought it was too simple to explain. They thought that anyone learning Haskell would have retained all the random topics that are contained in high school precalculus. They thought that anyone learning Haskell would be the Kind Of Person who just "naturally" remembers that sort of stuff.&lt;/p&gt;

&lt;p&gt;And now, because it's a woman writing this, you're probably assuming that this is a rant about how you don't need to know math to computer.&lt;/p&gt;

&lt;p&gt;Think again. I am That Kind Of Person. I started programming at either twelve or eight, depending on whether you count HyperCard. I learned Scheme in ninth grade. I am That Kind of Person, and the problem still made me feel like a fucking idiot. Like an impostor. Like I ought to just give up.&lt;/p&gt;

&lt;p&gt;Over whether I could remember offhand that vector cross products were not commutative, and what implications this had for turn direction problems.&lt;/p&gt;

&lt;p&gt;This could be a rant about the arrogance of the functional programming ivory tower. But a lot of people have made that rant already, and I'm more interested in self-reflection than in being yet another Ruby programmer who’s snotty about non-Rubyists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closer to Home
&lt;/h2&gt;

&lt;p&gt;I teach people to program in my free time. I co-organize a drop-in group called Learn Ruby in DC, so every other week I walk people of all different skill levels through how to code. This includes honest-to-god raw beginners, as well as apprentices and juniors.&lt;/p&gt;

&lt;p&gt;There is one understanding gap that every single one of them runs into at some point. They mix up local variables and instance variables, or variables and strings, or ivars and symbols — the exact mixup doesn't matter. They respond by randomly adding and subtracting &lt;code&gt;@&lt;/code&gt;s and colons and quotation marks until it works, and then they sigh relief. This happens because they don’t have mental scaffolding for the differences between variables and symbols, between locals and ivars — to them, they’re just words that may or may not have magic before them, and so you fiddle with the magic until it runs.&lt;/p&gt;

&lt;p&gt;It always takes me a few minutes of observation to figure out that that's what's happening in their heads. I’ve been writing Ruby since 2008; the scope differences between raw ivars and accessors and local variables are like breathing now. My intuitive brain assumes that everyone knows this, and I need to back up and remind it that that’s not true.&lt;/p&gt;

&lt;p&gt;I need to do this even though in 2008, as a woman who’d worked with various conventional languages for 8 years, I'd struggled like hell to internalize that difference myself.&lt;/p&gt;

&lt;p&gt;Am I making my students feel stupid? I hope not, but… sometimes I probably am.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do we go?
&lt;/h2&gt;

&lt;p&gt;The educational materials we create encode our values -- often accidentally. &lt;em&gt;_why's Poignant Guide&lt;/em&gt; reflects an ethos where programming is a joyful, creative activity. &lt;em&gt;Learn You a Haskell&lt;/em&gt; conveys a worldview that fat jokes are hilarious fun. &lt;em&gt;Real World Haskell&lt;/em&gt; assumes that all truly educated people remember vector-math intricacies off the top of their heads.&lt;/p&gt;

&lt;p&gt;The values of our educational materials create our community values. People whose assumptions mesh with theirs proceed onward to community participation; people whose assumptions and values don't leak out of the pipeline. I think Haskell is a really pretty language, but I can't tell if the Haskell community wants me.&lt;/p&gt;

&lt;p&gt;It’s human to forget that knowledge one has internalized is not common knowledge. It's human to be a poor teacher. It's human to confuse students; to make them feel terrible. It's still cruel. And when we encode that cruelty into our educational materials -- however accidentally -- we turn surviving that cruelty into something we value above all.&lt;/p&gt;

&lt;p&gt;What implicit knowledge is needed in your language of choice? How do you deal with that around juniors?&lt;/p&gt;




&lt;p&gt;If you found this post helpful, I'd love it if you subscribed to &lt;a href="https://www.wecohere.com/newsletter?utm_campaign=haskell-vectors-implicit-knowledge&amp;amp;utm_medium=devto&amp;amp;utm_content=footer"&gt;my company's 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>haskell</category>
      <category>beginners</category>
      <category>functional</category>
      <category>mentorship</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>
    <item>
      <title>Pair programming problems are not a smell.</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Fri, 16 Nov 2018 20:42:38 +0000</pubDate>
      <link>https://dev.to/cohere/pair-programming-problems-are-not-a-smell-4oia</link>
      <guid>https://dev.to/cohere/pair-programming-problems-are-not-a-smell-4oia</guid>
      <description>&lt;p&gt;Earlier this year, Betsy and I sat down and wrote down a few dozen types of dysfunctional behaviors we’ve experienced while pair programming, then sorted them into categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Taking up too little space (ex: lost but not speaking up)&lt;/li&gt;
&lt;li&gt;Taking up too much space (ex: keyboard hog)&lt;/li&gt;
&lt;li&gt;Pair has left you behind (ex: they’re on their phone and you don’t know why)&lt;/li&gt;
&lt;li&gt;You’ve left your pair behind (ex: you’re tunnel visioned and typing without talking)&lt;/li&gt;
&lt;li&gt;Out of sync with pair (ex: over-correcting the other person’s typos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Descriptions sorted into categories.... these looked a lot like programming smells! this was familiar territory!  Programming smells are solved with recipes, and this was a pattern we could follow.  We set out to create a recipe index, but quickly found that even a single recipe creation was impossible.&lt;/p&gt;

&lt;p&gt;Here’s the thing.  Programming smells and recipes work because they are scenarios that can be addressed without understanding the &lt;em&gt;why&lt;/em&gt;.  It doesn’t really matter &lt;em&gt;why&lt;/em&gt; some code ended up with a primitive obsession, you shove that obsession into a new class and everything works out great.  Like programming smells, dysfunctional pairing behaviors are an early warning system that indicate a change is warranted.  But that’s where the similarity ends.  People are much more complex than programs, and &lt;em&gt;why&lt;/em&gt; a pairing problem is manifesting changes how to address it. If your pair seems unengaged, treating them as a problem to be solved will make them feel worse.&lt;/p&gt;

&lt;p&gt;Instead of looking at unengagement and other pairing dysfunctions as smells, we should look at them as &lt;em&gt;symptoms&lt;/em&gt;. Smells lead us to clean up an area.  When you have a smelly house, you can clean it up room by room. &lt;em&gt;Symptoms&lt;/em&gt; show a system not operating as expected. When you have a system that’s not working as expected, you have to understand the problem before you can start to address it.&lt;/p&gt;

&lt;p&gt;Let’s take a deeper look at one of the pairing symptoms we identified: your pair seems unengaged. Here are just a few reasons why your pair might seem unengaged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn’t understand what you’re doing in this moment&lt;/li&gt;
&lt;li&gt;Doesn’t understand the problem&lt;/li&gt;
&lt;li&gt;Doesn’t know how to contribute&lt;/li&gt;
&lt;li&gt;Doesn’t want to take up too much space&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all really different reasons that could be behind the same symptom! Addressing pairing symptoms is kind of similar to addressing health symptoms: you’re more likely to be effective if you know what you’re treating. A few weeks ago I had a bad headache, and painkillers weren’t effective.  It turned out my allergies were being really bad, and I needed allergy meds, not headache meds, to address my symptom and make the headache go away. In order to help your pair become engaged, you need to pick the right approach based on why they might not seem engaged.&lt;/p&gt;

&lt;p&gt;If you have a good working relationship with your pair, you can probably stop and ask. &lt;em&gt;Hey, you doing ok? you’re more distracted than usual.&lt;/em&gt; But if you don’t already have a lot of pre-existing trust in your relationship, it’s not that simple. Your pair might not trust you enough to talk with you about it.&lt;/p&gt;

&lt;p&gt;How about picking a neutral-seeming action? Maybe you could suggest they drive?  Even that might not go over so well. If you don’t know why your pair seems unengaged, asking them to drive might not address the symptom. What if the cause was that they weren’t sure what you were doing and were scared to ask?  And if your pair’s scared to ask, that’s an additional symptom as well..&lt;/p&gt;

&lt;p&gt;It might seem silly for your pair to be scared.  But your pair has had different life and education and career  experiences leading up to your current pairing, and that may give them good reason to be scared:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poor past experience with speaking up&lt;/li&gt;
&lt;li&gt;Poor past experience with displaying ignorance&lt;/li&gt;
&lt;li&gt;Inexperience with pair programming&lt;/li&gt;
&lt;li&gt;Discomfort with the expected learning style (maybe your team does a code tour, but they feel more comfortable with sitting and reading on their own)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Betsy and I set out to find solutions to common pairing dysfunctions that we could share in a new workshop.  What we learned is that there is no one-size-fits-all answer for any of it.  How you and your pair have been encouraged and discouraged throughout your life and career have an enormous impact on how you will behave and respond while pairing.  You don’t need to know anyone’s past to be a better pair for them in the present.  You do need to know not everyone has had the same positive or negative past experiences you’ve had. When you carry that awareness with you into your interactions is when you can be a better pair.&lt;/p&gt;




&lt;p&gt;If you liked this article, Cohere publishes &lt;a href="https://www.wecohere.com/newsletter?utm_campaign=pair-programming-is-not-a-smell&amp;amp;utm_source=devto" rel="noopener noreferrer"&gt;a monthly Engineering Leadership newsletter and we would be &lt;em&gt;delighted&lt;/em&gt; if you signed up&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>coding</category>
      <category>productivity</category>
      <category>leadership</category>
      <category>devtips</category>
    </item>
    <item>
      <title>A Brief Introduction to GraphQL and Rails</title>
      <dc:creator>Zee</dc:creator>
      <pubDate>Fri, 02 Nov 2018 02:25:48 +0000</pubDate>
      <link>https://dev.to/cohere/a-brief-introduction-to-graphql-and-rails-250i</link>
      <guid>https://dev.to/cohere/a-brief-introduction-to-graphql-and-rails-250i</guid>
      <description>&lt;p&gt;Over the past 9 months, we've applied GraphQL and Rails as the API platform on client projects. Most of these projects rely on rapid iteration across both the user interface and the data model as we follow the signals for product market fit. Rails and ActiveRecord continue to provide incredibly flexible data management and persistence techniques that is well complemented by GraphQL's fitness as a &lt;a href="https://www.informit.com/articles/article.aspx?p=359417&amp;amp;seqNum=3" rel="noopener noreferrer"&gt;seam&lt;/a&gt; for stubbing out fake data, rolling out new fields, and deprecating existing fields in a safe way that is decoupled from the persistence layer.&lt;/p&gt;

&lt;p&gt;That said, GraphQL in general and GraphQL in Rails in particular have a few caveats worth considering before jumping on the train:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;While the marketing will tell you the concepts behind GraphQL are simple, GraphQL is quite complex. GraphQL is a distributed database composed of four major parts: a strongly typed data model, a persistence and retrieval layer, a transportation layer, and a client (many of which have caches!). Understanding each of those things independently is difficult enough, but the interactions and subtle tensions between them brings the complexity to a higher level.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GraphQL is significantly less opinionated about design than &lt;a href="https://en.wikipedia.org/wiki/Representational_state_transfer" rel="noopener noreferrer"&gt;REST&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/HATEOAS" rel="noopener noreferrer"&gt;HATEOS&lt;/a&gt;. This forces us to make more decisions about how to design our API and how to make changes happen than relying on the &lt;a href="https://jsonapi.org" rel="noopener noreferrer"&gt;json:api spec&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The GraphQL ecosystem still feels slightly to the left of the break even point on &lt;a href="https://en.wikipedia.org/wiki/Technology_life_cycle" rel="noopener noreferrer"&gt;technology maturity life cycle&lt;/a&gt;. The community is vibrant and growing. Plus, they're investing in tooling that provide quality of life improvements at a breakneck pace (a blessing and a curse).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All that being said, if you choose to join the ranks of &lt;a href="https://developer.github.com/v4/" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="https://help.shopify.com/en/api/custom-storefronts/storefront-api/graphql" rel="noopener noreferrer"&gt;Shopify&lt;/a&gt;, and others who are building GraphQL APIs on top of Rails apps there are a few suggestions to help lighten your load.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Join the &lt;a href="https://graphql-slack.herokuapp.com/" rel="noopener noreferrer"&gt;GraphQL Slack&lt;/a&gt;. Yes, we are all a bit overwhelmed by Slack Overflow, but the community there is both friendly and helpful. Plus, it's where the developers of the &lt;a href="http://graphql-ruby.org/" rel="noopener noreferrer"&gt;GraphQL Ruby library&lt;/a&gt; hang out. Having direct access to people working in Ruby and GraphQL has leveled me up quite a bit faster than relying purely on my own experience.&lt;/li&gt;
&lt;li&gt;Take a gander at the &lt;a href="https://github.com/wecohere/graphql-rails-example" rel="noopener noreferrer"&gt;GraphQL Rails Example&lt;/a&gt; that pulls some of the lessons learned about implementing and testing a Rails GraphQL server I’ve gleaned over the last 6 months. Yes, the feature tests are using &lt;a href="https://github.com/cucumber/cucumber-js" rel="noopener noreferrer"&gt;cucumber-js&lt;/a&gt; instead of Ruby. That’s because 99% of the work we’ve been doing is in react-native and vue apps with &lt;a href="https://www.apollographql.com/" rel="noopener noreferrer"&gt;Apollo&lt;/a&gt; as the client, and doing feature testing with the same stack your client app uses is immensely useful at finding and isolating odd corner cases.&lt;/li&gt;
&lt;li&gt;Read through &lt;a href="https://gist.github.com/swalkinshaw/3a33e2d292b60e68fcebe12b62bbb3e2#input-structure-part-1" rel="noopener noreferrer"&gt;this excellent guide to designing GraphQL APIs by the folks at Shopify&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Accept that it takes time to find the grain of GraphQL, both as a design philosophy and the Rails implementations particular idioms. I’ve found that my learning has been very much stepwise, with plateaus that eventually result in conceptual breakthroughs.&lt;/li&gt;
&lt;li&gt;Embrace service objects. Mutations can quickly become complex, firing off events of their own and making multiple data changes at once. While my mutations and even some queries start as methods, they are quickly get extracted to objects that can be tested at the rspec level once they reach a dozen or so lines of code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall, I’m happy with how well Rails and GraphQL apps serve as the core for an API-driven&lt;br&gt;
platform.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>graphql</category>
    </item>
  </channel>
</rss>
