<?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: Anna Rankin</title>
    <description>The latest articles on DEV Community by Anna Rankin (@annarankin).</description>
    <link>https://dev.to/annarankin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F8919%2F11503403.jpeg</url>
      <title>DEV Community: Anna Rankin</title>
      <link>https://dev.to/annarankin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/annarankin"/>
    <language>en</language>
    <item>
      <title>Framing your Narrative</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Tue, 18 Feb 2020 01:01:48 +0000</pubDate>
      <link>https://dev.to/annarankin/framing-your-narrative-3eg</link>
      <guid>https://dev.to/annarankin/framing-your-narrative-3eg</guid>
      <description>&lt;p&gt;As the number of folks transitioning into tech from previous careers climbs, I think it's important that we train ourselves to look at our pasts for the catalysts that lead us here. Over the years, I've noticed that the ability to create a coherent narrative out of my personal and employment history has been extremely helpful, both for staying personally motivated and for convincing employers that I'll be a good fit for their team. &lt;/p&gt;

&lt;h2&gt;
  
  
  What use is a narrative?
&lt;/h2&gt;

&lt;p&gt;Human beings like narratives! Storytelling presents events in a logical, ordered way - as opposed to chaos, which generally &lt;a href="https://www.theatlantic.com/health/archive/2015/03/how-uncertainty-fuels-anxiety/388066/"&gt;makes us anxious&lt;/a&gt;. We can take advantage of this mental bias to help ourselves (and our future employers!) see ourselves as on a path to our new careers. &lt;/p&gt;

&lt;p&gt;Consider these two stories:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Anna Rankin decided to pursue art in college after a clerical error put her in an "Art 101" class instead of Chorus. After graduating, she spent two years in a government internship doing graphic design. In 2011, she moved to China to teach English to young children. On her return to the USA in 2013, she worked as a Starbucks barista for several months before being rehired by her boss from her former internship. After taking a 12-week coding course, she took a job at the company that ran the class and has worked for them since.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...versus:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Anna Rankin still remembers her first programming experience at the age of four, when her father taught her how to make randomly sized circles appear on the screen of their monstrous desktop PC. From teaching herself how to code for an online game to creating websites in plain text to showcase her artwork at 12, her activities always tended toward technical creativity. After obtaining her degree in illustration and teaching ESL abroad, she felt herself drawn to both education and programming as a craft. While learning to code at General Assembly, she found a community of like-minded people. She joined their engineering team and now works directly on the ed-tech products that students (just like her in the past) use today!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What differences do you notice between these two descriptions? How does reading each make you &lt;em&gt;feel?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To me, the first of these comes across as sterile; directionless. It's almost like someone took a LinkedIn history and turned it into sentences! There's no real sense of direction or agency. Since there's no reasoning provided for each new career choice, I might even think this Anna person is a bit flighty or scatter-brained.&lt;/p&gt;

&lt;p&gt;The second tells a strong, coherent story with descriptive, &lt;em&gt;relevant&lt;/em&gt; examples from the past. It leaves out irrelevant details in order to paint a concise picture of who I am now and what's important to me. More than that, it seems like a reasonable progression to a logical conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if I don't &lt;em&gt;have&lt;/em&gt; a consistent story?
&lt;/h2&gt;

&lt;p&gt;You have a vast number of experiences that form who you are by the time you're an adult—the ones you choose to focus on shape who you are now. I'm not asking you to retcon your personal history or make things up to seem like a better potential employee, but instead, to sift through the things that are important to you and showcase them as steps toward to your current goal.&lt;/p&gt;

&lt;p&gt;For example: If you're coming into technology as an artist, what processes and perspectives do you bring to programming? If you're a former chef, what &lt;a href="https://dev.to/vickilanger/cooking-programming-1o9p"&gt;analogies can you draw&lt;/a&gt; from your past experience to your current pursuits? Have you navigated difficult interpersonal relationships with skill and aplomb? No matter what field you come from, chances are that you learned something you can apply to your new career.&lt;/p&gt;

&lt;p&gt;I still remember my first job interview. I was terrified; going in for a graphic design internship as a traditional (pastel, paint) artist. My interviewer (and later boss) hit me with a tough question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why should I hire you when you don't have any experience with graphic design? What sets you apart from the applicants who have already worked with the programs we use?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I told her that my experience as an illustrator and my ability to compose evocative images set me apart - that a program was just a tool, and I was confident that I could create quality graphics regardless of the medium (not nearly this eloquently I am &lt;em&gt;so&lt;/em&gt; sure). My boss told me years later that this answer was why she chose me for the role. My ability to frame my capability in terms of my existing experience &lt;em&gt;literally&lt;/em&gt; got me the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can this help me with my impostor syndrome?
&lt;/h2&gt;

&lt;p&gt;Framing your journey as a story can also help a lot with personal motivation. When all you can see in your history feels like a jumble of unrelated jobs and irrelevant skills, it's easier to get overwhelmed and want to throw in the towel. Discovering a consistent narrative is calming; inspiring. Looking back at my past through the lens of that second bio, it almost seems like landing at my current job was an inevitable result of everything that led up to it. In the same way, your past has been leading you inexorably toward exactly where you are now!&lt;/p&gt;

&lt;p&gt;In her book &lt;a href="https://www.goodreads.com/book/show/28170973-rocking-the-boat"&gt;"Rocking the Boat,"&lt;/a&gt; Debra Meyerson presents her research into how individual and systemic change is created in organizations. When discussing how powerful it is to frame small wins as part of larger initiatives, she notes that "...people's understanding of what an action means can be every bit as important as the action itself." This holds true for our personal histories as well! Without framing, the events in your past are just stark facts, ready to be interpreted in any way your audience sees fit. As the only person in the world who has lived your life, you are the best suited individual to provide context on how it's unfolding. Providing this context gives you the power to assign specific meaning to your existence rather than letting other people define you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's try it!
&lt;/h2&gt;

&lt;p&gt;As an experiment, I'd like you to try and create yourself a quick elevator pitch (three to five sentences). Ask yourself these questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can you remember the first experience that interested you in technology? What was that like?&lt;/li&gt;
&lt;li&gt;What parts of your past experience or employment have prepared you to become a technologist? What are you &lt;em&gt;really good&lt;/em&gt; at? What are you excited to bring to a team?&lt;/li&gt;
&lt;li&gt;What moves you? What are you really excited about for the present or future?&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;I learned how to write a good bio and present my story in a compelling way at &lt;a href="https://www.globaldiversitycfpday.com/"&gt;Global Diversity CFP day&lt;/a&gt; and &lt;a href="https://www.writespeakcode.com/"&gt;Write/Speak/Code&lt;/a&gt; events—two excellent organizations ♥ Also, if you're in the market for a job, I highly recommend &lt;a href="https://cattsmall.com/advice/2018/09/24/be-hireable-get-hired.html"&gt;this read on showing off your hireability&lt;/a&gt; (and also &lt;a href="https://www.youtube.com/watch?v=B3Qj_f1UrmA"&gt;this talk on storytelling because it's also good&lt;/a&gt;) from the inimitable &lt;a href="https://cattsmall.com/"&gt;Catt Small!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>storytelling</category>
    </item>
    <item>
      <title>RubyConf 2019 Sketchnotes</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Sat, 23 Nov 2019 16:20:22 +0000</pubDate>
      <link>https://dev.to/annarankin/rubyconf-2019-sketchnotes-21i8</link>
      <guid>https://dev.to/annarankin/rubyconf-2019-sketchnotes-21i8</guid>
      <description>&lt;p&gt;I had a fantastic time this year at &lt;a href="https://rubyconf.org/about" rel="noopener noreferrer"&gt;RubyConf&lt;/a&gt; in Nashville! This year, I committed to trying out &lt;a href="https://www.verbaltovisual.com/what-is-sketchnoting/" rel="noopener noreferrer"&gt;&lt;em&gt;sketchnoting,&lt;/em&gt;&lt;/a&gt; or visual note-taking. I had a great time at &lt;a href="https://www.writespeakcode.com/2019/program/" rel="noopener noreferrer"&gt;Write/Speak/Code&lt;/a&gt; this past August at &lt;a href="http://www.deniseyu.io/art" rel="noopener noreferrer"&gt;Denise Yu&lt;/a&gt;'s excellent workshop "Telling stories with doodles" and have been meaning to experiment ever since!&lt;/p&gt;

&lt;p&gt;Other sketchnoters I highly admire:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/chiuki/timelines/580188517080272896" rel="noopener noreferrer"&gt;Chiu-Ki Chan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/search?q=(sketchnote)%20(from%3A%40nitya)&amp;amp;src=typed_query&amp;amp;f=live" rel="noopener noreferrer"&gt;Nitya Narasimhan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=dHyht32yysc" rel="noopener noreferrer"&gt;Amruta Renade&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a selection of my notes from the conference:&lt;/p&gt;

&lt;h2&gt;
  
  
  Principles of Awesome APIs and How to Build Them
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://keavy.com/" rel="noopener noreferrer"&gt;Keavy McMinn&lt;/a&gt;&lt;br&gt;
Excellent presentation on API development best practices!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fodwa81abglaee8al2zro.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fodwa81abglaee8al2zro.JPG" alt="Sketchnote of McMinn's presentation on API development best practices"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ElasticSearch 5 &lt;del&gt;or&lt;/del&gt; and Bust!
&lt;/h2&gt;


&lt;div class="ltag__user ltag__user__id__"&gt;
    &lt;div class="ltag__user__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F99mvlsfu5tfj9m7ku25d.png" alt="[deleted user] image"&gt;
    &lt;/div&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;[Deleted User]&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Molly's talk walked us through an ElasticSearch upgrade gone awry - and the recovery process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frlemcznk19lolxdofhnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frlemcznk19lolxdofhnk.png" alt="Sketchnote of Struve's presentation on an ElasticSearch upgrade gone awry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  OOP in Pictures
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://inem.at/" rel="noopener noreferrer"&gt;Ivan Nemytchenko&lt;/a&gt;&lt;br&gt;
Nemytchenko's presentation detailed creating and using a visual language to teach OOP concepts -- with robots!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F29q42dd4bihno3ds0dx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F29q42dd4bihno3ds0dx1.png" alt="Sketchnote of Nemytchenko's presentation on creating and using a visual language to teach OOP concepts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby &amp;amp; Sentiment Analysis
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/RabbiGreenberg" rel="noopener noreferrer"&gt;Ben Greenberg&lt;/a&gt;&lt;br&gt;
Ben demonstrated how to use free tools to create an app that tells you the "mood" of the news for the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpb31onmsws9shq2b74y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpb31onmsws9shq2b74y.png" alt="Sketchnote of Greenberg's demonstration of using free tools to create an app that tells you the mood of the news for the day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens when a Linguist Learns to Code?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://erica.tech/" rel="noopener noreferrer"&gt;Erica Sosa&lt;/a&gt;&lt;br&gt;
Erica gave an overview of her journey from linguistics to programming, as well as going over the concepts that language learners in both fields deal with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw9qz06hrk9mctwopi7r3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw9qz06hrk9mctwopi7r3.png" alt="Sketchnote of Sosa's presentation on her journey from linguistics to programming, and the concepts that language learners in both fields deal with"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing Performance and Memory Problems
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/fglc2" rel="noopener noreferrer"&gt;Frederick Cheung&lt;/a&gt;&lt;br&gt;
Frederick's talk led us through the process of finding sneaky memory leaks and gave us tools for hunting down the culprits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3yh90jj791sr32hy6xj6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3yh90jj791sr32hy6xj6.png" alt="Visual notes for the presentation "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fun, Friendly Computer Science
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.mercedesbernard.com/" rel="noopener noreferrer"&gt;Mercedes Bernard&lt;/a&gt;&lt;br&gt;
Mercedes walked us through several key computer science concepts in an approachable way -- very helpful for interviews!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7x2yhdpq72dfw3nr4sk2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7x2yhdpq72dfw3nr4sk2.png" alt="Visual notes for the presentation "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Late, Over Budget, and Happy
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/amynewell" rel="noopener noreferrer"&gt;Amy Newell&lt;/a&gt; &amp;amp; &lt;a href="https://twitter.com/natbudin" rel="noopener noreferrer"&gt;Nat Budin&lt;/a&gt;&lt;br&gt;
I very much enjoyed Amy and Nat's heartfelt and honest presentation of the lessons they learned during a skunkworks project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8yjl7oypc0488hez5jdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8yjl7oypc0488hez5jdi.png" alt="Visual notes for the presentation "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lucky You
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.sandimetz.com/" rel="noopener noreferrer"&gt;Sandi Metz&lt;/a&gt;&lt;br&gt;
Sandi dropped a &lt;em&gt;ton&lt;/em&gt; of data on us for the third day's keynote, reminding us how very &lt;em&gt;lucky&lt;/em&gt; we are to be in software - and how the demographic someone is born into is even more important than luck.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw6fl1sj1zt8mrep3gfco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw6fl1sj1zt8mrep3gfco.png" alt="Visual notes for the presentation "&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>conferences</category>
      <category>sketchnotes</category>
      <category>ruby</category>
    </item>
    <item>
      <title>You might think a pirate's favorite programming language is R...</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Sat, 06 Jul 2019 03:58:05 +0000</pubDate>
      <link>https://dev.to/annarankin/you-might-think-a-pirate-s-favorite-programming-language-is-r-494d</link>
      <guid>https://dev.to/annarankin/you-might-think-a-pirate-s-favorite-programming-language-is-r-494d</guid>
      <description>&lt;p&gt;...but his first love be the C! 🌊&lt;/p&gt;

</description>
      <category>jokes</category>
    </item>
    <item>
      <title>Equality of Data Structures: Ruby vs. JavaScript</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Sun, 07 Apr 2019 15:48:12 +0000</pubDate>
      <link>https://dev.to/annarankin/equality-of-data-structures-ruby-vs-javascript-48b7</link>
      <guid>https://dev.to/annarankin/equality-of-data-structures-ruby-vs-javascript-48b7</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/annarankin/equality-of-data-structures-ruby-vs-javascript-48b7#tldr"&gt;Skip to the TL;DR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In recent weeks I've been reading through &lt;a href="https://leanpub.com/javascriptallongesix" rel="noopener noreferrer"&gt;JavaScript Allongé&lt;/a&gt; by Reginald Braithwaite (an excellent look at the fundamentals of JS through a functional programming lens) with a coworker. My coworker brought up something that didn't make sense to her:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In JavaScript, why isn't an empty array equal to an empty array? What's the difference between the two identical objects?&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;To someone who's been working with JavaScript for a while, the answer might seem straightforward: "They're two different arrays; of course one is not equal to the other!" A Rubyist like my friend, however, may see it differently: "These two arrays have the same content—what do you &lt;em&gt;mean&lt;/em&gt; they're not equal?" As I see it, this is a philosophical difference between the meanings of the comparison operators in Ruby and JavaScript. One language uses the concept of object &lt;em&gt;equivalence&lt;/em&gt; to compare data structures, while the other checks explicitly for object &lt;em&gt;identity.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I'm not going to discuss the &lt;a href="https://codeburst.io/javascript-double-equals-vs-triple-equals-61d4ce5a121a" rel="noopener noreferrer"&gt;JS double equal&lt;/a&gt; or &lt;a href="https://stackoverflow.com/a/4467823" rel="noopener noreferrer"&gt;Ruby's threequal&lt;/a&gt; operators, as these are more than just simple comparison operators (I also avoid using them because they can be confusing and misleading!).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fm0qtdk4jjp60buay16ke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fm0qtdk4jjp60buay16ke.png" alt="Pixel art version of the Ruby programming language's logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Equality of Data Structures in Ruby
&lt;/h2&gt;

&lt;p&gt;When you compare data structures using the built-in behavior of &lt;code&gt;==&lt;/code&gt; in Ruby, you're really comparing the &lt;em&gt;contents&lt;/em&gt; of the object—and in the case of an array, you're also checking that the order of the elements is the same in both. This means that two variables that point to different objects in memory &lt;strong&gt;could be equal.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first_array&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;second_array&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If for some reason you truly want to check and see that two variables refer to the same object, you can check object's ID—or preferably, use the &lt;code&gt;.equal?&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first_array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;second_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt; &lt;span class="c1"&gt;# 70176467875700 == 70176467875680&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;second_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# a clearer way to perform this comparison&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike in JavaScript, the comparison operator in Ruby is actually a method defined on the class you're comparing (awesome walkthrough of this concept &lt;a href="http://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/45-more-classes/lessons/105-equality_of_objects" rel="noopener noreferrer"&gt;here&lt;/a&gt;). Since &lt;code&gt;==&lt;/code&gt; is just a method, you can even override it if you like! This is a reasonable practice if you're writing custom classes that need to be compared to each other. Silly example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ditto&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;==&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;     &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ditto&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Pikachu'&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ditto&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ditto&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq4mb7fi1ukyk6o9vqt96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq4mb7fi1ukyk6o9vqt96.png" alt="Pixel art version of the JavaScript Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Equality of Data Structures in JavaScript
&lt;/h2&gt;

&lt;p&gt;Unlike Ruby, JavaScript does not expose a unique ID for objects because it doesn't &lt;em&gt;need&lt;/em&gt; to. Data structures are compared by identity by default. If two variables are equal, you can be sure that they point to the same object in memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;firstArray&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;secondArray&lt;/span&gt;
&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to check and see if two separate data structures have the same content, you'll have to write your own logic to check—or use a function from a library like &lt;a href="https://lodash.com/docs/4.17.11#isEqual" rel="noopener noreferrer"&gt;Lodash&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Super naïve implementation:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arraysAreEqual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;array2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;array1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;array2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;arraysAreEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;arraysAreEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;JavaScript's &lt;code&gt;===&lt;/code&gt;&lt;/strong&gt; checks to see if the two variables it's comparing &lt;em&gt;point to the same data structure,&lt;/em&gt; while &lt;strong&gt;Ruby's &lt;code&gt;==&lt;/code&gt;&lt;/strong&gt; method checks to see if the &lt;em&gt;contents&lt;/em&gt; of two arrays or hashes are equivalent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Farqmcciw9n5tfekra6yt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Farqmcciw9n5tfekra6yt.png" alt="RB vs. JS Equality - first image of two closed boxes &amp;amp; JavaScript logo, text "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ruby's &lt;code&gt;[1,2,3] == [1,2,3]&lt;/code&gt; translates to &lt;code&gt;[1,2,3].every((el, index) =&amp;gt; el === [1,2,3][index])&lt;/code&gt; in JS.&lt;/p&gt;

&lt;p&gt;JavaScript's &lt;code&gt;[1,2,3] === [1,2,3]&lt;/code&gt; translates to &lt;code&gt;[1,2,3].equal?([1,2,3])&lt;/code&gt; in Ruby.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Hopefully this helps you wrap your mind around what the two different languages expect when comparing data structures! If you're interested in going deeper, I've put together some references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Strict_equality_using" rel="noopener noreferrer"&gt;MDN docs on equality and sameness in JS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dorey.github.io/JavaScript-Equality-Table/" rel="noopener noreferrer"&gt;A fun JavaScript equality table&lt;/a&gt; (bonus points for the &lt;code&gt;if()&lt;/code&gt; explanations!)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://ruby-for-beginners.rubymonstas.org/operators/comparison.html" rel="noopener noreferrer"&gt;Intro to Ruby's Comparison Operators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/45-more-classes/lessons/105-equality_of_objects" rel="noopener noreferrer"&gt;RubyMonk's custom &lt;code&gt;.==&lt;/code&gt; method example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Nevertheless, Anna Still Codes</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Mon, 11 Mar 2019 20:20:08 +0000</pubDate>
      <link>https://dev.to/annarankin/nevertheless-anna-still-codes-fc6</link>
      <guid>https://dev.to/annarankin/nevertheless-anna-still-codes-fc6</guid>
      <description>&lt;p&gt;In 2019, I continue to code, mentor, and write because I love the challenge—I've left previous careers due to boredom and lack of engagement. My job is a lot of things, but boring is sure not one of them!&lt;/p&gt;

&lt;p&gt;I think I do deserve some credit for the amount of personal growth I've packed into the past year. I've practiced my writing, improved my knowledge around lower-level tools and infrastructure, and taken some big strides in demonstrating leadership and responsibility on my team and within my organization as a whole. I've pushed through some intense stress and anxiety to overcome my own limitations (and discovered brand new limitations!).&lt;/p&gt;

&lt;p&gt;This year, I hope to see even more intermediate and advanced community-led programming training proliferate in tech! We could all use more mid-level and senior developers, but we won't see them shine until we help them grow.&lt;/p&gt;

</description>
      <category>wecoded</category>
    </item>
    <item>
      <title>Tips for Self-Reviews</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Thu, 31 Jan 2019 14:52:36 +0000</pubDate>
      <link>https://dev.to/annarankin/tips-for-self-reviews-29ce</link>
      <guid>https://dev.to/annarankin/tips-for-self-reviews-29ce</guid>
      <description>&lt;p&gt;It's performance review season for me and the rest of the folks at General Assembly — a time of year that lends itself to introspection (along with a host of other emotions). Everyone approaches reviews differently: with trepidation, excitement, annoyance, indifference. I try to look forward to sharing feedback with my manager and teammates — it's great to have a process, as well as regular, dedicated time to reflect on personal progress and performance, measured in a way that's meaningful to &lt;em&gt;me.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Though not all review processes include the exact steps or methods I talk about below, I believe that general ideas (honesty, self-kindness, and specificity) still apply. I'm not going to talk about giving feedback to others (this post is long enough already 😂), but instead focus on self-evaluation, goal-setting, and making good use of the feedback you receive. Below are some tips that I've found useful as I've gone through this process over the years — what to include, where to look for salient information about your performance, and how to use feedback you're given in a valuable way.&lt;/p&gt;

&lt;p&gt;At GA, our performance review cycle looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Employees write a self-review and a review of their manager&lt;/li&gt;
&lt;li&gt;Coworkers review each other (anonymously)&lt;/li&gt;
&lt;li&gt;Managers review their direct reports (along with writing their own self-review)&lt;/li&gt;
&lt;li&gt;Managers and employees meet to discuss all this feedback and talk about next steps&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Self Review
&lt;/h2&gt;

&lt;p&gt;The dreaded &lt;em&gt;self-review!&lt;/em&gt; 😱 Even with specific questions outlined for me to answer, this is the hardest part of the process for me. Rather than writing my responses as paragraphs, I generally create an outline of bullet points and fill in  relevant supporting details as I go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calling out the good stuff
&lt;/h3&gt;

&lt;p&gt;Like many developers, I deal with impostor syndrome on a daily basis. Initially, it was very difficult for me to call out my strengths and accomplishments without anxiety (or immediately diminishing them in the next sentence). I still struggle with the fear of coming off as arrogant or overconfident when I list the things I'm proud of doing. Some things that really helped me out were: &lt;/p&gt;

&lt;h4&gt;
  
  
  Find concrete records of your past work
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you use a system like JIRA or Trello to track what you do, you can use this to reflect on your completed work. A lot can happen in six months - chances are there are some accomplishments you forgot about hiding in that list of tickets!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;About a year and a half ago, I started tracking my daily to-dos in a markdown file (I use &lt;a href="https://bear.app/"&gt;Bear&lt;/a&gt;, but I've talked to folks who use Google Docs, the Apple Notes app, and Evernote to great success). I also use this file to track "shoulder-taps" (day-to-day requests for help or clarification), meetings, and other tasks along with the approximate time I spent on them. This kind of low-level detail is fun and useful for me to look back over when I'm thinking about what I spent the majority of my time doing (and is a good place to see patterns!).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you don't have a robust work tracking system, try looking through work-related emails and dated documents. Chances are there's enough there to jog your memory!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Review past work artifacts
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;This is mostly applicable if you're involved in creating documentation or putting together proposals/project briefs, but can also apply to PRs you've created and features you've helped implement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Look at new skills you've picked up
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Maybe you converted your project's front-end to React from jQuery or debugged a hairy database problem; maybe you started working on server logic for the first time. Anything you've done for the first time should be recorded as a win!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go beyond technical accomplishments. Anything that affects your performance and value as an employee is fair game — leading a project, giving a talk, participating in mentorship, or writing blog posts (😉) are all important to factor into your overall performance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Being honest about areas that need growth
&lt;/h3&gt;

&lt;p&gt;Looking at parts of yourself that need to change can be a painful and humbling experience, but is absolutely essential to your personal growth. It's vital to know what needs improvement so that you can best focus your efforts on moving to the next level (personally AND professionally). Dan Abramov's recent post &lt;a href="https://overreacted.io/things-i-dont-know-as-of-2018/"&gt;"Things I Don't Know as of 2018"&lt;/a&gt; was a really cool example of an incredibly accomplished developer assessing gaps in his technical knowledge. With a concrete list of things you want to work on, choosing goals becomes a lot easier.&lt;/p&gt;

&lt;p&gt;It's just as important not to dwell on your shortcomings. Like any bug, they should be acknowledged, triaged, and eventually a plan should be made to address them. Here are a few categories I've focused on in the past:&lt;/p&gt;

&lt;h4&gt;
  
  
  Technical skills
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Is there a part of the codebase you wish you knew more about? Maybe you're interested in your team's deployment setup, but only have a cursory knowledge of what goes on. Do you want to work on application architecture skills or feel you lack an understanding of the tools you use on a daily basis? Anything you want to level up in is a good choice here. &lt;/li&gt;
&lt;li&gt;Remember to be &lt;em&gt;very specific&lt;/em&gt; — "I suck at Ruby" or "I want to get better at deploying things" are less valuable than "I want to improve my understanding of OOP at a high level and make better design decisions in practice (example here)" or "I want to understand how we automate deployments and the steps that go into pushing new code to production."&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Workflow improvements
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Everyone (well, &lt;em&gt;almost&lt;/em&gt; everyone) says they need to get better at "time management." Drill deeper and question if that's &lt;em&gt;really&lt;/em&gt; the problem. Maybe it's actually a problem with distractions, how you track your time, or how you plan out your day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Think about parts of the development process where you frequently feel friction. For example, I struggled for some time to get my PRs reviewed and merged in a timely manner. I made a commitment to let no more than two days go by without movement on a PR, and am proud to say that I've gotten much braver about reminding my coworkers that I need their feedback and much better about taking action on that feedback quickly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Leadership &amp;amp; mentorship
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you're interested in leading teams or projects, consider bringing this up in your review. Taking on even a small leadership role can be a powerful way to learn from your peers and demonstrate competence. For example, I took on the task of starting and leading our bi-monthly JavaScript "council" meetings at work. I was intimidated and anxious at first, but after some format failures and experimentation, we managed to find our groove! I love knowing that these meetings have real value to my coworkers (which also serves as a huge motivator to keep them valuable).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mentorship is a very personal goal for me. If you're excited about education or want to help mentor folks who are newer to the field, you should definitely set a goal to do so. It doesn't need to be formal, either! I've learned a lot from "unofficial" mentoring and being mentored, and it can be very fulfilling to give back to the tech community.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Goals
&lt;/h2&gt;

&lt;p&gt;All this reflection (combined with the feedback you get from others) means nothing unless you do something with it! Once you've had some time to process (and look over, if applicable) any feedback you receive, talk with your manager about what your focus will be for the next six months. Set three to five concrete goals based on what you, your peers, and your manager laid out in the reviews. Good goals are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specific.&lt;/strong&gt; Vague goals are harder to complete than goals with concrete acceptance criteria (much like work tickets 🤔). Consider replacing "Get better at Python" (for example) with a step you want to take in that direction, like "Attend two Python meetups a month" or "Finish that advanced Python course I bought three months ago."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Focused.&lt;/strong&gt; Set yourself up for success rather than trying to improve every single thing you can think of. The saying that "If everything's important, &lt;em&gt;nothing&lt;/em&gt; is important" is applicable here. Give the goals you choose to work on the importance they deserve by giving yourself enough bandwidth to work on each of them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Personally meaningful.&lt;/strong&gt; Avoid choosing goals based solely on what your manager/another party thinks you should do — or what you think you're &lt;em&gt;supposed&lt;/em&gt; to choose. If your goals aren't truly yours, your motivation to complete them will be lacking (and they'll be more likely to go undone because of it). Even worse, you're choosing to invest your time in something you might not care about instead of something you're really excited about. (One caveat here: if your manager tells you that you &lt;em&gt;need&lt;/em&gt; to work on something, you should absolutely work on it — and work on understanding its importance to your daily work.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Out of your comfort zone.&lt;/strong&gt; Don't be afraid to push yourself a little when setting goals. You might be surprised to find what you can accomplish in a single review period if you set ambitious goals! Do work with your manager to make sure they're attainable, though. For example, it might not be practical for you to give four talks in the next four months, or read ten new programming books (or maybe it is!). Just make sure you're got the time and support to achieve what you set out to do.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  At the end of the day
&lt;/h2&gt;

&lt;p&gt;Whatever your experience with performance reviews has been or what the process at your company looks like, I hope these tips are helpful to you. It's worth noting that this advice works best in organizations with a healthy, candid feedback culture. As with any bundle of advice, please use with caution and awareness of the environment you operate in.&lt;/p&gt;

</description>
      <category>career</category>
      <category>culture</category>
      <category>learning</category>
    </item>
    <item>
      <title>Ways of Seeing: ORMS &amp; SQL Views</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Thu, 22 Nov 2018 04:51:03 +0000</pubDate>
      <link>https://dev.to/annarankin/ways-of-seeing-orms--sql-views-562o</link>
      <guid>https://dev.to/annarankin/ways-of-seeing-orms--sql-views-562o</guid>
      <description>&lt;p&gt;This article assumes a general understanding of how databases, Ruby, and Rails work - the examples below use the &lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;ActiveRecord&lt;/a&gt; library to map database relations to Ruby objects in the context of a Rails application. Check out the &lt;a href="https://github.com/annarankin/vet-orm-ruby-example" rel="noopener noreferrer"&gt;example repository&lt;/a&gt; to see them in action! (Note: I have also set up a JavaScript example &lt;a href="https://github.com/annarankin/vet-orm-example" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you're interested, but I haven't found a JS ORM that has first-class support for SQL views.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregation Aggravation
&lt;/h2&gt;

&lt;p&gt;Web applications often display "summaries," or aggregated data, that may pull in information from and make calculations across several database tables. ORM (Object-Relational mapping) database modeling libraries are generally designed to read from one table at a time. While this pattern is useful for tons of use cases (basic CRUD for the win), we run the risk of complex application code and/or expensive DB querying when we try to aggregate data. SQL views can potentially help us cut down on the number of queries we make while pushing our data aggregation logic down into the database.&lt;/p&gt;

&lt;p&gt;For example - imagine you've been hired to create an application that helps veterinarians communicate with their clients. A workflow like this (pared-down) example this might be familiar to you if you've created a server endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:pets&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;pets: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4zjlhpsgpet8kls7r53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4zjlhpsgpet8kls7r53.png" alt="seated dog on blue background" width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code above is pretty straightforward: it loads up a user, loads up their pets, and serializes the data in the format the front end expects. We're making two queries, but they're simple and (hopefully) fast — that's a reasonable expectation.&lt;/p&gt;

&lt;p&gt;Fast forward a few weeks, and we've got a new requirement. Users can see which pets they have registered with you, but they also want to know "at-a-glance" when their next appointment is! So, you update the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:pets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Pre-load the user's pets' appointments that are scheduled for the future&lt;/span&gt;
&lt;span class="n"&gt;upcoming_appointments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appointments&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;date: :asc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date &amp;gt;= ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;pets: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# Use Array#find to return the first of this pet's appointments&lt;/span&gt;
    &lt;span class="n"&gt;next_appointment_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upcoming_appointments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;appt&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;appt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pet_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;next_appointment_date: &lt;/span&gt;&lt;span class="n"&gt;next_appointment_date&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honestly, this still isn't &lt;em&gt;too&lt;/em&gt; bad. We've added another query — this time for appointments — with some ordering and filtering logic. We're also adding in some looping logic in our serialization step to pull out the first appointment for each of the owner's pets. It's a bit untidy, but hey, &lt;em&gt;it works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Personally, I don't like encoding all of this behavior in the application code. It feels messy to me, and it's far too easy for something nefarious to sneak in. For example, imagine we'd done this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;pets: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;next_appointment_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appointments&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;date: :asc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date &amp;gt;= ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;next_appointment_date: &lt;/span&gt;&lt;span class="n"&gt;next_appointment_date&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🙀Oh no! Someone snuck a query into our serialization step, and while this &lt;em&gt;might&lt;/em&gt; not ring any alarm bells &lt;em&gt;at first&lt;/em&gt; (when you're dealing with one or two pets per user), what happens when the local animal shelter becomes one of your clients and registers over a hundred pets? Over a hundred queries per page load.&lt;/p&gt;

&lt;p&gt;This is obviously a contrived example, but I've run into more complex cases where N+1 queries were hidden away in service objects and separate files. I personally like to push this kind of logic down a level — into the database - when it starts getting more complicated or when I need it elsewhere in my application. That's where SQL views come in!&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;em&gt;is&lt;/em&gt; a SQL view?
&lt;/h2&gt;

&lt;p&gt;My mental model of a SQL view at its most basic is "a saved query in your database that acts like a table." There's &lt;a href="https://www.essentialsql.com/what-is-a-relational-database-view/" rel="noopener noreferrer"&gt;a lot&lt;/a&gt; &lt;a href="https://www.periscopedata.com/blog/how-to-use-views" rel="noopener noreferrer"&gt;more to it&lt;/a&gt; than that, but this simple understanding can get you a long way. For example, if I executed the following statement in my database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;silly_users&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' Mc'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'erson'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;silly_name&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;a href="http://www.postgresqltutorial.com/postgresql-concat-function/" rel="noopener noreferrer"&gt;More on the &lt;code&gt;||&lt;/code&gt; concatenation operator&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I can query results from this view using the same syntax I would for a table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;silly_users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="n"&gt;silly_name&lt;/span&gt;       
&lt;span class="c1"&gt;-------+------------+------------------------&lt;/span&gt;
     &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Melissa&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Melissa&lt;/span&gt; &lt;span class="n"&gt;McMelissaerson&lt;/span&gt;
     &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Colleen&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Colleen&lt;/span&gt; &lt;span class="n"&gt;McColleenerson&lt;/span&gt;
     &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Vince&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Vince&lt;/span&gt; &lt;span class="n"&gt;McVinceerson&lt;/span&gt;
     &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;David&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;David&lt;/span&gt; &lt;span class="n"&gt;McDaviderson&lt;/span&gt;
     &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Dennis&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Dennis&lt;/span&gt; &lt;span class="n"&gt;McDenniserson&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this view acts pretty much like a table, it can play really nicely with an ORM. We can create a view-backed model!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;silly_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SillyUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;silly_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;silly_name&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Melissa McMelissaerson'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using Ruby, I highly recommend the &lt;a href="https://github.com/thoughtbot/scenic" rel="noopener noreferrer"&gt;Scenic gem by ThoughtBot&lt;/a&gt;, which brings view versioning and other helpful features to projects that use the ActiveRecord ORM.&lt;/p&gt;

&lt;h2&gt;
  
  
  A New Perspective
&lt;/h2&gt;

&lt;p&gt;Let's try writing a database view to grab the data we want instead of querying for it in our application code &lt;a href="https://robots.thoughtbot.com/ordering-within-a-sql-group-by-clause" rel="noopener noreferrer"&gt;(more here on using &lt;code&gt;DISTINCT ON&lt;/code&gt; with &lt;code&gt;GROUP BY&lt;/code&gt;&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Grabs one record per pet, returning the earliest future appointment date &lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;pets_with_upcoming_appointments&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appointments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;next_appointment_date&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;pets&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;appointments&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pet_id&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;appointments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Great! Now we can read from this view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pets_with_upcoming_appointments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pet_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pet_name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;next_appointment_date&lt;/span&gt;   
&lt;span class="c1"&gt;---------+--------+----------+-----------------------&lt;/span&gt;
       &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Flannery&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
       &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Porkchop&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
       &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Gravy&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
       &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Magnus&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; 
       &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Huey&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
       &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Duey&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
       &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Louie&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pets_with_upcoming_appointments&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pet_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pet_name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;next_appointment_date&lt;/span&gt;   
&lt;span class="c1"&gt;---------+--------+----------+-----------------------&lt;/span&gt;
       &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Flannery&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've got the view set up, we could create a migration using the Scenic gem mentioned earlier, then hook it up to a database-backed ORM model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetWithUpcomingAppointment&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pets_with_upcoming_appointments'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our view has a &lt;code&gt;user_id&lt;/code&gt; field, it's trivial to hook up a relation in our User model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:pets&lt;/span&gt;
  &lt;span class="c1"&gt;# wooooot&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:pet_with_upcoming_appointments&lt;/span&gt;
  &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:appointments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;through: :pets&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can clean up our data-fetching application code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:pet_with_upcoming_appointments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;pets: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pet_with_upcoming_appointments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;next_appointment_date: &lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_appointment_date&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not bad! We're back down to two queries and no ordering/date comparison logic in the controller 💪 Even better, we can re-use this model in other parts of our application without duplicating the querying logic. This really starts to shine when you start performing more complex aggregation and pulling in data from different tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls of SQL views
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftw2kk4d4eu49c77z00jy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftw2kk4d4eu49c77z00jy.png" alt="cat peeking up out of a hole" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I'm clearly a fan, there are some things you should watch out for when you start thinking about views in your application:&lt;/p&gt;

&lt;h3&gt;
  
  
  Overzealous viewification
&lt;/h3&gt;

&lt;p&gt;This is another facet of the "I have a hammer, everything's a nail!" problem. When you first start playing around with them, you can find yourself pushing &lt;em&gt;too&lt;/em&gt; much logic down into the database (where it's abstracted away and harder to find). You wind up having to run a lot of migrations because every time you need to change what your application serves up, you need to update or create a new view. 😬 Before you turn whatever you're working on into a view, ask yourself if the benefit you're gaining is worth the trade-off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Too many levels of cascading views
&lt;/h3&gt;

&lt;p&gt;Views are generally made up of results from tables, but you can also create a view that pulls in data from another view (A META VIEW!?). This can seem like a good idea ("I'll define what an 'active' user is in this view and then pull that in everywhere I need a user!"), but in practice, I've seen it make updating views at all levels more difficult. You can also drive yourself up a wall trying to figure out how a change in one view can affect another one that pulls in data from several levels away 😵 This is one that's really only a problem in tandem with the first point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Propagated inefficiency
&lt;/h3&gt;

&lt;p&gt;If you're familiar with profiling your SQL queries and using &lt;code&gt;EXPLAIN&lt;/code&gt;/&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; to find issues (or if that &lt;code&gt;LEFT JOIN appointments&lt;/code&gt; in the view above made you squirm 😂), this is a good time to put those skills to use! If &lt;a href="https://www.tutorialspoint.com/postgresql/postgresql_indexes.htm" rel="noopener noreferrer"&gt;indexes&lt;/a&gt;, &lt;a href="https://malisper.me/postgres-hash-joins/" rel="noopener noreferrer"&gt;hash joins&lt;/a&gt;, and &lt;a href="https://malisper.me/postgres-sequential-scans/" rel="noopener noreferrer"&gt;sequential scans&lt;/a&gt; make your head spin, you might end up with seemingly simple queries running very slowly once you start to get more records in your database. Inefficient queries can end up supporting a ton of your app's functionality if you're not careful, leading to sluggish performance overall. At the least, check out the impact introducing a view has on the speed of the process that's using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What now?
&lt;/h2&gt;

&lt;p&gt;If you're interested, here are some resources I'd recommend (and the links I've referenced throughout this post) to learn more! Live long, prosper, and fear not the database 🖖&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/annarankin/vet-orm-ruby-example" rel="noopener noreferrer"&gt;Example Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.essentialsql.com/what-is-a-relational-database-view/" rel="noopener noreferrer"&gt;What is a Relational Database View?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.periscopedata.com/blog/how-to-use-views" rel="noopener noreferrer"&gt;How to Use Views&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://robots.thoughtbot.com/ordering-within-a-sql-group-by-clause" rel="noopener noreferrer"&gt;Ordering Within a SQL Group By Clause&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://explain.depesz.com/" rel="noopener noreferrer"&gt;Depesz's tool for explaining &lt;code&gt;EXPLAIN&lt;/code&gt; output&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tutorialspoint.com/postgresql/postgresql_indexes.htm" rel="noopener noreferrer"&gt;Intro to Indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://malisper.me/postgres-hash-joins/" rel="noopener noreferrer"&gt;Hash Joins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://malisper.me/postgres-sequential-scans/" rel="noopener noreferrer"&gt;Sequential Scans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://malisper.me/postgres-index-scans/" rel="noopener noreferrer"&gt;Index Scans&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sql</category>
      <category>ruby</category>
      <category>rails</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Graph in SQL Land</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Mon, 08 Oct 2018 01:38:47 +0000</pubDate>
      <link>https://dev.to/annarankin/building-a-graph-in-sql-land-hi6</link>
      <guid>https://dev.to/annarankin/building-a-graph-in-sql-land-hi6</guid>
      <description>&lt;p&gt;In the spirit of sharing failures and learning from them, I'd like to tell you a story about a time a younger me helped create a "clever" system to organize a content hierarchy. This cautionary tale is mostly for fun, but also to document some of the neat things and painful lessons I learned along the way. Names and entities have been changed to protect the innocent 😜&lt;/p&gt;

&lt;h2&gt;
  
  
  But ...why?
&lt;/h2&gt;

&lt;p&gt;Long, long ago in a product team far away, there existed a project to create a new, more flexible hierarchy for collections of books. Originally, our data was set up like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ft5bahvoa7w36sq4n7myu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ft5bahvoa7w36sq4n7myu.png" alt="original data setup - collections, genres, and books"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A user had access to a collection of books ("Fiction" in this case); a collection contained several genres, which in turn contained individual books. A user could be granted access to see everything within a collection, and could create "reading lists" so they could favorite/categorize books based on their own criteria.&lt;/p&gt;

&lt;p&gt;This worked well - a user could see all the fiction books in the collection, and they could even add these books to a personalized reading list. Later on, however, our product team started testing out a new collection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwwqmg75m7jffpkb3wj0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwwqmg75m7jffpkb3wj0b.png" alt="same diagram, but with a new "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We didn't initially want to allow &lt;em&gt;all&lt;/em&gt; users to access the new "Nonfiction" collection, so we granted access to that separately.&lt;/p&gt;

&lt;p&gt;We had a few issues - mainly that a user could only access and create reading lists from one collection at a time, and they had to switch between them manually in the UI. This worked well when we only had one collection, but lacked flexibility when we started adding more. Additionally, if a book appeared in more than one genre, its entry would need to be duplicated - our data structure didn't expect a book to have more than one genre 😬 Users were also prevented from adding books to their reading lists that didn't belong in any of our collections at all yet. If they wanted a one-stop shop to track what they were reading, they'd be out of luck.&lt;/p&gt;

&lt;p&gt;The idea was that as we added more collections, we needed a better way to categorize our content and avoid duplication. Folks from across the organization worked together to create a new content structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3cnn0ur0ebledvcotbt3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3cnn0ur0ebledvcotbt3.png" alt="proposed content hierarchy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rather than segmenting our content into "collections," we'd house everything in a mega-collection - called the "library" - and allow books to appear in multiple genres. We hoped this would make our content more discoverable in searches, less duplicative, and would allow users to have reading lists with both fiction AND non-fiction titles on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technologies
&lt;/h3&gt;

&lt;p&gt;We were already working with some tried-and-true technologies in this application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; database (most SQL databases have some form of support for database views)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://guides.rubyonrails.org/active_record_basics.html" rel="noopener noreferrer"&gt;ActiveRecord&lt;/a&gt; within a &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Ruby on Rails&lt;/a&gt; application as an ORM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, our job as engineers was to implement the product vision and provide the tools our coworkers needed to interact with this new model. We had a choice: Do we create a series of discrete tables linked to each other by standard relations, or do we try &lt;em&gt;something new?&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;(spoiler alert - we tried something new.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Going Graph
&lt;/h2&gt;

&lt;p&gt;It didn't take us long to decide that if we were going to create this new "world view," we wanted to play with some cool new technology along the way - and to be fair, it did seem like a graph model would serve the current use case well. We drew out some concepts of what we expected our entities and relations to look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flbyt3lpkpas6cspmz5vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flbyt3lpkpas6cspmz5vo.png" alt="image of graph containing library, reading list, genres and books a user can access"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Users would be able to add their own books to their reading lists if they so chose - but that didn't mean they belonged in the library's collection.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That done, we discussed implementation - how would we bring this into our application? How would this fit into our current codebase? After some research and exploration, it was decided that we'd avoid bringing in a true graph database (like Neo4j, OrientDB, etc.). Cost was potentially an issue, and we wanted to avoid forcing our developers to learn &lt;a href="https://neo4j.com/developer/get-started/" rel="noopener noreferrer"&gt;new concepts&lt;/a&gt; and a &lt;a href="http://tinkerpop.apache.org/docs/current/reference/#intro" rel="noopener noreferrer"&gt;new query language&lt;/a&gt; until we were sure this model was something that we really wanted to stick with.&lt;/p&gt;

&lt;p&gt;That having been decided, we drew up our approach to storing our graph entities and relations ("nodes" and "edges") in our relational database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjb9orgc3kd8f6jgl7p68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjb9orgc3kd8f6jgl7p68.png" alt="table setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Good
&lt;/h2&gt;

&lt;p&gt;Once we got our new hierarchy set up, rendering nested relationships became simple. We could easily convert a higher-level model (like a genre) into a tree-like JSON representation (in the real world, we had several additional levels to deal with - think category, subcategory, etc. - so this was pretty helpful).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;library&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; &lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Library'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;genres: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Fantasy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;books: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Book 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="p"&gt;},{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Book 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Interacting with the data also became pretty simple - our nodes all had the same attributes, so we used pretty generalized views and services to display, create, and edit everything from the library and reading lists to genres and books.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Editing &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'node-edit-form'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;node: &lt;/span&gt;&lt;span class="vi"&gt;@node&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another bonus was getting to understand graph structures pretty well. We learned how to traverse a graph, how to filter a graph, how to check for cyclical references - and more. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Bad
&lt;/h2&gt;

&lt;p&gt;Everything worked well in the beginning - when everything looked the same. Before too long, though, we found that we needed additional information in the payloads we sent down to the front end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Our data model started to become more complex...&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Library'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;genres: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Fantasy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;book_count: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;books: &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Book 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;available_for_checkout: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;author: &lt;/span&gt;&lt;span class="s1"&gt;'Bob Bobberson'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="p"&gt;},{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'Book 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;available_for_checkout: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;author: &lt;/span&gt;&lt;span class="s1"&gt;'Frances Farina'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;We started mixing behavior and additional attributes in with structure of the library hierarchy rather than allowing the graph structure to do what it was good at - defining the relationships of entities to one another. We started shoehorning in details about individual nodes, complicating the logic required to render our over-generalized views. These views quickly became complicated and full of switches on node type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Editing &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Genre'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Book count: &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;book_count&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'node-edit-form'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;node: &lt;/span&gt;&lt;span class="vi"&gt;@node&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;elif&lt;/span&gt; &lt;span class="vi"&gt;@node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Book'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'book-node-edit-form'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;node: &lt;/span&gt;&lt;span class="vi"&gt;@node&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'node-edit-form'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;node: &lt;/span&gt;&lt;span class="vi"&gt;@node&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a while, it became clear that our application was a graph database that we'd smashed into the shape of a bookstore, instead of a bookstore that utilized a graph database to store data. Our codebase was littered with overly generic, meaningless methods and controllers that were difficult at best to understand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Instead of this...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_book&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Clearly saving a book to the DB&lt;/span&gt;
  &lt;span class="n"&gt;save_to_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# we ended up with this:&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# What are we saving here??&lt;/span&gt;
  &lt;span class="no"&gt;Nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Awful
&lt;/h2&gt;

&lt;p&gt;As the library grew in size, querying became downright awful. Queries ballooned in complexity; complicated preloading was required in order to avoid making hundreds of queries. Many of our services relied on recursion to generate serialized JSON or aggregate data, which added mental overhead when trying to figure out what your bug was, what was causing it, and where your extra queries were being made. When new devs joined our team, it was harder for them to ramp up on what we were doing, which caused frustration and wasted a lot of time. We used tools like recursive SQL views and terrifying "octo-UI" (see below) admin tools that caused more grief than joy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnborilsqh0hmmtxr24m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnborilsqh0hmmtxr24m7.png" alt="image of a force-directed graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This graph is similar to a stopgap admin interface we implemented to allow admins to interact with the graph - screenshot taken from &lt;a href="https://github.com/d3/d3/wiki/Gallery" rel="noopener noreferrer"&gt;the D3 example gallery&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The last straw(s)
&lt;/h2&gt;

&lt;p&gt;All told, we worked with this psuedo-graph structure for a little under a year before we gave up the ghost and started ripping it out. Some notable reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As mentioned above, writing services and views for a general idea of a "node" did not work well once our data model evolved&lt;/li&gt;
&lt;li&gt;This idea of graph-like structures stored in a relational database was certainly not supported by ActiveRecord - this resulted in inefficient and confusing queries&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Super importantly&lt;/em&gt;, we (as developers) didn’t create tooling that would have made these concepts easy to work with, for other developers and for the end users of our product.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;While I wouldn't do this again, I certainly learned a few things along the way.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recursive views are cool 🤓&lt;/li&gt;
&lt;li&gt;Never put your personal curiosity ahead of someone else's livelihood.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use the right tool for the job!&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next time you find yourself looking at a shiny new technology and feel the desire to use it in a product that's critical to your company's day-to-day business, I encourage you to consider your choices carefully. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do I even &lt;em&gt;need&lt;/em&gt; INSERT SHINY THING HERE? ...do I &lt;em&gt;really?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most often, it's in your best interest to choose "boring technology" (&lt;a href="http://mcfunley.com/choose-boring-technology" rel="noopener noreferrer"&gt;&lt;em&gt;Choose Boring Technology,&lt;/em&gt; Dan McKinley&lt;/a&gt;) over "the new hotness." If you consistently opt for exciting, innovative technologies over longer-lived and widely-understood systems, you're going to increase the cost/headache related to onboarding new engineers and maintaining your entire system. &lt;/p&gt;

&lt;p&gt;I would argue that this also applies to the &lt;em&gt;patterns&lt;/em&gt; we use to build our software. When we hid the implementation details of our graph structure poorly, we introduced code that felt unintuitive and confused new developers. If we had done a better job isolating that code, it would have been much easier to swap out our "homegrown" version for the real thing later on.&lt;/p&gt;

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

&lt;p&gt;The article linked above makes a great case for limiting the number of technical solutions you use as a team. Instead of spending your energy troubleshooting and maintaining a homegrown solution, you can choose to focus on solving business problems - &lt;em&gt;actual people's problems.&lt;/em&gt; Add too many unfamiliar technologies, and you run the risk of having one or two "experts" (masters of the arcane) on how your application works rather than a straightforward codebase that most folks can get up to speed on in a reasonable amount of time.&lt;/p&gt;

&lt;p&gt;If the honest answer to "do I need this?" is "yes, this is by far the best solution for my problem," ensure you have the time, support, team bandwidth, and expertise to do so. If these conditions are met, then you need to do your research - and please, use the right tools for the job 🙇‍♀️&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>programming</category>
      <category>cautionarytale</category>
    </item>
    <item>
      <title>Finally in Promises &amp; Try/Catch</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Wed, 19 Sep 2018 00:43:06 +0000</pubDate>
      <link>https://dev.to/annarankin/finally-in-promises--trycatch-2c44</link>
      <guid>https://dev.to/annarankin/finally-in-promises--trycatch-2c44</guid>
      <description>&lt;p&gt;Lately, I've been experimenting more with the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; keywords in JavaScript. I noticed that I sometimes struggle to reconcile the strategies I use with Promises with the way I need to write code in the newer syntax. Most recently, I was playing around with &lt;code&gt;finally&lt;/code&gt; in some &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; blocks and came upon some behavior I didn't expect.&lt;/p&gt;

&lt;p&gt;This post assumes a general understanding of how asynchronous JavaScript code works - particularly, how Promises work. (If you're looking for an in-depth explanation of async JS from callbacks to the async/await keywords, there's a pretty good overview on &lt;a href="https://javascript.info/async" rel="noopener noreferrer"&gt;javascript.info&lt;/a&gt; - you can also check out &lt;a href="https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9" rel="noopener noreferrer"&gt;Mostafa Gaafar's article&lt;/a&gt; for some of the neat features of async/await.)&lt;/p&gt;

&lt;p&gt;For context - in the JavaScript codebase I spend a lot of my time in, we've historically dealt with asynchronous actions by using Promises heavily. Generally, this pattern is a lot more familiar to me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetchSomeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;doSomethingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;logAndReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is less familiar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchSomeData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;doSomethingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;logAndReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;code&gt;finally&lt;/code&gt;...?
&lt;/h1&gt;

&lt;p&gt;You'll notice that a &lt;code&gt;finally&lt;/code&gt; callback/block is missing from both of the examples above. I don't use either often in my code, which led me to a misunderstanding (of both, really). Let's dive into the differences between this concept in Promises and in try/catch!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;finally&lt;/code&gt; in Promises
&lt;/h2&gt;

&lt;p&gt;When you use the &lt;code&gt;somePromise.then(x).catch(y).finally(z)&lt;/code&gt; pattern, your business logic is generally happening in the &lt;code&gt;then&lt;/code&gt; callback (&lt;code&gt;x&lt;/code&gt;, above - what you want to do once &lt;code&gt;somePromise&lt;/code&gt; has resolved) or in the &lt;code&gt;catch&lt;/code&gt; callback (&lt;code&gt;y&lt;/code&gt; above - returns what you want to pass along in case something goes horribly wrong). You might never have even used &lt;code&gt;finally&lt;/code&gt; in your code - and that's fine. &lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally#Description" rel="noopener noreferrer"&gt;MDN docs&lt;/a&gt;, a &lt;code&gt;finally&lt;/code&gt; callback allows you to execute logic once your Promise has been settled - resolved or rejected - one way or the other. It has absolutely no impact on the &lt;em&gt;value&lt;/em&gt; that your promise will resolve to - it doesn't even have access to it. In fact, the documentation states that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Promise.resolve(2).finally(() =&amp;gt; {})&lt;/code&gt; will be resolved with &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means (somewhat counterintuitively) you can sprinkle &lt;code&gt;finally&lt;/code&gt; callbacks liberally throughout your promise chain without changing the final result that it will resolve to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Please don't do this 😅&lt;/span&gt;

&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;some&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WHALE HELLO THERE 🐋&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;anAdditional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Looks like we made it past the first step 🙏&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;yetAnother&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;thing added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;We're done I think 🙌&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Final result:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this code, you should see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftwm87a53dtplm3wzf0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftwm87a53dtplm3wzf0x.png" alt="result of executing the above code: Each step is logged to the console sequentially; the final result is an object containing three keys" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;finally&lt;/code&gt; in try/catch blocks
&lt;/h2&gt;

&lt;p&gt;The try/catch/finally pattern has been around for a &lt;em&gt;long&lt;/em&gt; time in JavaScript - since version 1.4 (ES3 specification, around 1999). There are a couple of logical parallels I'd drawn between this pattern and how promises are handled:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;try&lt;/code&gt;/&lt;code&gt;then&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
This is where our "happy path" logic goes - if nothing breaks, all the action happens here!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;catch&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
This is where we end up when things go wrong, and gives us a chance to redeem ourselves 🙏&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;finally&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
This logic will execute after the &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;then&lt;/code&gt; (and possibly &lt;code&gt;catch&lt;/code&gt;) logic has completed. This code runs no matter what, whether we've encountered an error or not.&lt;/p&gt;

&lt;p&gt;The difference here that tripped me up is related to &lt;code&gt;return&lt;/code&gt; statements. If your &lt;code&gt;finally&lt;/code&gt; block &lt;em&gt;does not&lt;/em&gt; include a return statement, it has &lt;em&gt;no effect&lt;/em&gt; on the return value. However, if you return a value from a &lt;code&gt;finally&lt;/code&gt; block, that value will override all other returns and be the final result of your function. (Check out &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#Returning_from_a_finally_block" rel="noopener noreferrer"&gt;this example&lt;/a&gt; from the docs!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This worked as I expected.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returnFromTryCatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someFunction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;someFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Caught an error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This block has no effect on the return value.&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All done!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This was a surprise to me!&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returnFromFinally&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someFunction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;someFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Caught an error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Wait... so I'm just swallowing my return and error handling?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All done!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes sense, but it felt inconsistent to me. My experience with Promises reared its head - why should a &lt;code&gt;finally&lt;/code&gt; block &lt;em&gt;ever&lt;/em&gt; be allowed to override the value a function returns?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmcv30ovktlvkub0n9iu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdmcv30ovktlvkub0n9iu.png" alt="pixel art of a woman looking at her laptop in deep thought" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Finding the Reason
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Semantic_satiation" rel="noopener noreferrer"&gt;&lt;em&gt;Finally,&lt;/em&gt;&lt;/a&gt; I pinged my tech lead detailing my annoyance, and he sent me &lt;a href="https://stackoverflow.com/questions/3837994/why-does-a-return-in-finally-override-try" rel="noopener noreferrer"&gt;a link to a related StackOverflow discussion&lt;/a&gt;. Seeing the ECMAScript specification (emphasis mine) for this behavior helped it settle into place in my brain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The production &lt;code&gt;TryStatement : try Block Finally&lt;/code&gt; is evaluated as follows:&lt;/p&gt;

&lt;p&gt;Let B be the result of evaluating Block.&lt;br&gt;
Let F be the result of evaluating Finally.&lt;br&gt;
&lt;strong&gt;If F.type is normal, return B.&lt;/strong&gt;&lt;br&gt;
Return F.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(It's worth noting the "completion types" according to the &lt;a href="https://tc39.github.io/ecma262/#sec-completion-record-specification-type" rel="noopener noreferrer"&gt;ECMAScript spec&lt;/a&gt; are "One of normal, break, continue, return, or throw" - I have assumed that a function that does not include a &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt;, or &lt;code&gt;throw&lt;/code&gt; keyword qualifies as "normal." Kind of weird semantics there.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Note on Multiple Return Statements
&lt;/h3&gt;

&lt;p&gt;The code samples in this post do not utilize a single return. I'm not going to get too far into the debate around multiple return statements - I will say that in general, having a single return for longer functions has served me well in the past, but I've found them less helpful in shorter blocks. It probably would have made my life easier in this case, though!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>"No Config" Bundling with Parcel</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Fri, 10 Aug 2018 18:40:03 +0000</pubDate>
      <link>https://dev.to/annarankin/no-config-bundling-with-parcel-5dl0</link>
      <guid>https://dev.to/annarankin/no-config-bundling-with-parcel-5dl0</guid>
      <description>&lt;p&gt;This workshop will focus on using Parcel - a web application bundler - to bundle up and serve a Typescript &amp;amp; React project without writing a configuration file! Parcel is a quick and lightweight alternative to bundlers like Webpack and Rollup. We'll discuss bundling in general, dive into some source code, and go over the steps required to set up and add dependencies to our application.&lt;/p&gt;

&lt;p&gt;All levels are welcome! Prior experience with a bundler is helpful for context, but not required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;0:00: Introduction&lt;/li&gt;
&lt;li&gt;4:30: TLDR; Bundlers&lt;/li&gt;
&lt;li&gt;5:55: Ways of creating production code&lt;/li&gt;
&lt;li&gt;7:40: Enter the Webpack!&lt;/li&gt;
&lt;li&gt;10:12: What you get with Parcel&lt;/li&gt;
&lt;li&gt;11:55: Let's try it! Starting from Scratch&lt;/li&gt;
&lt;li&gt;20:00: "Automagically" Installed Dependencies&lt;/li&gt;
&lt;li&gt;29:30: What about production?&lt;/li&gt;
&lt;li&gt;30:35: Writing a configuration file&lt;/li&gt;
&lt;li&gt;33:25: The Takeaway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/presentation/d/1cscgpQGQcCuXCUwCP8_L7RO0_LJ_eutVNm05IdKZOuY/edit?usp=sharing"&gt;Click here for slides!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This DEV Live Stream was recorded on July 23, 2018&lt;/em&gt;&lt;br&gt;
Join the next Live Stream. See all upcoming events &lt;a href="https://dev.to/events"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devlive</category>
    </item>
    <item>
      <title>How Art School Prepared Me for Programming</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Tue, 22 May 2018 03:39:05 +0000</pubDate>
      <link>https://dev.to/annarankin/how-art-school-prepared-me-for-programming-33l2</link>
      <guid>https://dev.to/annarankin/how-art-school-prepared-me-for-programming-33l2</guid>
      <description>&lt;p&gt;At the startup where I work, a lot of my coworkers in the engineering department have great stories about where they came from - organic chemistry, education, computer science, publishing, data, etc. - the list goes on. My personal experience before transitioning into software was in traditional illustration: mostly pastels, oils, and acrylics.&lt;/p&gt;

&lt;p&gt;While I don't normally catch flack for having a non-traditional background, I know a few folks who associate art solely with emotional expression, and technology exclusively with math and logic. I'm of the opinion that programming is as much an art as it is a science or a utility. In that vein, I wanted to share a few lessons I learned in art school, but use in my job every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can often communicate better through abstraction.
&lt;/h2&gt;

&lt;p&gt;When I mention "abstract art," a lot of folks bring up &lt;a href="http://www.theartstory.org/artist-pollock-jackson.htm"&gt;Jackson Pollock&lt;/a&gt;, &lt;a href="http://www.theartstory.org/artist-mondrian-piet.htm"&gt;Mondrian&lt;/a&gt;, or some of the more "out-there" (read: usually cubist) works of &lt;a href="http://www.theartstory.org/artist-picasso-pablo.htm"&gt;Pablo Picasso&lt;/a&gt;. Personally, I'm really digging this quote I saw highlighted on Mondrian's page (linked above):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I wish to approach truth as closely as is possible, and therefore I abstract everything until I arrive at the fundamental quality of objects."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That idea is extremely appealing to me as a developer - the idea that I could strip away all the unnecessary details in a project until only the bare, beautiful, unconvoluted minimum remains. The less code there is in a project, the cleaner the lines of the whole; the simpler the shapes, the less muddied the colors. This doesn't mean there's a "right" number of lines or characters or methods for a project to have; only that I strive for elegance through simplicity (actually achieving that admittedly lofty goal is another matter 😁).&lt;/p&gt;

&lt;p&gt;As a pixel artist, I think about abstraction a lot - how much detail can I take away before my doodle becomes unreadable? What's important to include? I ask the same questions when I'm designing an interface for a class or when I'm writing documentation. I think (a lot 😳) about how much flexibility to allow for - how much configuration is too much? Not enough? How much complexity can I abstract away while maintaining the functionality and usefulness of a service? Documentation is hard, too - distilling nebulous chunks of shared and individual knowledge down into a manageable set of bullet points, examples, and/or diagrams is an art form all its own.&lt;/p&gt;

&lt;p&gt;The same concept holds true when you're trying to explain something verbally. Choosing the right level of detail for your audience - whether it's your co-workers or a class of programming newbies - allows you to communicate with them clearly. &lt;/p&gt;

&lt;p&gt;We sometimes talk about libraries or workflows as "black boxes" - I think this is a valuable tool and a great abstraction for working with beginners as well as experienced folks. It's nice to remember sometimes that we don't need to know how &lt;em&gt;everything&lt;/em&gt; works right now - we can give ourselves the luxury of tighter focus on what we're doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't be precious with your creations.
&lt;/h2&gt;

&lt;p&gt;There's a story that circulates through the students and alumni of my alma mater of one instructor's particularly challenging drawing class. For an entire semester, students work in pencil on paper, drawing the same model in the same pose for six hours a week. They pour their hearts and souls into this drawing, obsessing over light, form, and subtle gradations of shadow for months. At the end of the class, after a group critique, the instructor gives his final instruction: &lt;/p&gt;

&lt;p&gt;"Erase it."&lt;/p&gt;

&lt;p&gt;After all that hard work, the students rub out every painstaking pencil stroke until their papers are gray and featureless. Some call it cruel (I won't say it's not, honestly), but it teaches a very important lesson in an indelible way. Don't be precious with the product of your work - the important thing is that you &lt;em&gt;did&lt;/em&gt; it. You've learned from the experience and (as the instructor would say) - "You drew it once, you can draw it again."&lt;/p&gt;

&lt;p&gt;This idea has served me well in my work as a developer, from being critiqued in code reviews to seeing thousands of lines I wrote deleted from a project all in one fell swoop. You'll hear folks say "&lt;em&gt;You&lt;/em&gt; are not your code!" and it's true! The amount of amazing (and sometimes bizarre) knowledge I've gained from my past work is invaluable to me regardless of where it lives now. When someone points out a strange pattern or opportunity for improvement in my work, I have no compunctions about erasing my old "sketches" in favor of a better solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dissatisfaction with your work is a sign of developing taste.
&lt;/h2&gt;

&lt;p&gt;This one is very close to my heart. When I was in art school, I noticed a pattern in how I felt about my work: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I'd work really hard and create some pieces I was super proud of.&lt;/li&gt;
&lt;li&gt;I'd see the amazing work that my peers, mentors, or even famous artists created and start comparing my own output to theirs.&lt;/li&gt;
&lt;li&gt;Cue depression and dissatisfaction with my own work - everything I'd done before was childish and crude; my hands weren't capable of creating what I saw inside my head.&lt;/li&gt;
&lt;li&gt;After days/weeks/months of struggling with my own skill level, I'd start to create things I was proud of again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This probably sounds familiar to anyone who's learned how to code - at least, it was common among my students when I taught web development. One of my drawing teachers told me that taste and skill develop at different speeds, and at different times. If you can't see the flaws in your work, then you probably need to refine and develop your skill further. If you feel that what you've created is lacking, then your level of taste has simply progressed beyond your current level of ability. Thinking like this has helped me deal better with doubt and (mostly) fend off the dreaded impostor syndrome.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you've got any other stories of lessons you brought to your tech job from a seemingly unrelated field, I'd love to hear them.&lt;/p&gt;

</description>
      <category>art</category>
      <category>career</category>
    </item>
    <item>
      <title>Supplying Placeholder Data by Catching Promises</title>
      <dc:creator>Anna Rankin</dc:creator>
      <pubDate>Tue, 08 May 2018 00:14:55 +0000</pubDate>
      <link>https://dev.to/annarankin/supplying-placeholder-data-by-catching-promises-372l</link>
      <guid>https://dev.to/annarankin/supplying-placeholder-data-by-catching-promises-372l</guid>
      <description>&lt;p&gt;Recently, I wrote a simple Slack bot that queries an API for feedback on our products, then posts that feedback into a specified channel. The bot also allows users to vote on whether or not a piece of feedback is actionable for them (ie: "there's a typo in this lesson" vs. "ok" or "no comment").&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjl4qndzugyl136mswm1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjl4qndzugyl136mswm1c.png" alt="Screenshot of slack message - "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since this was a "Hack Day" project, the initial implementation lived up to its name and was very hacky - votes were not stored by the server; users could vote as many times as they wanted. The voting was handled by modifying the string that came in with a POST &lt;code&gt;/handle-vote&lt;/code&gt; request (Slack as a persistence layer 😂).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// text =&amp;gt; 'Yes: 0 No: 0'&lt;/span&gt;
&lt;span class="c1"&gt;// value =&amp;gt; 'yes' || 'no&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateAttachmentText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;votes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;updateAttachmentText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Yes: 0 No: 0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; 'Yes: 1 No: 0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hacky little bot turned out to be quite useful for our product team - but I knew its dark, terrible secrets and decided to write a more robust version that used Redis for storage of vote data - it would store voters' Slack user ids and prevent one user from voting multiple times.&lt;/p&gt;

&lt;p&gt;The bot itself uses a cron job to post new feedback to the channel. While upgrading, I added a step to that script to create a new "blank" record with that feedback's ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialVotes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;yes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;no&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialVotes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a user hits a button, the server receives the request, looks up the piece of feedback by its ID, adds the user's id to the correct list ('yes' or 'no'), then saves it back to the Redis store after performing some logic to ensure users can only vote once, and only one way.&lt;/p&gt;

&lt;p&gt;The issue here is with messages from the original bot - these bits of feedback don't have records associated with their IDs in our application; so the following code would fail if the user clicked on a vote button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Imagine our Redis client setup...&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// setup, etc&lt;/span&gt;

  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to find result for&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ... and a Vote loading class...&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; 
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;loadVote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteData&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;someRedisClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadVote&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;incrementCounterAndSave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;span class="c1"&gt;// Uncaught rejection :(&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially I thought this would be an annoying problem where I'd need conditional logic to handle the record not existing somewhere in my server code. Looking at the &lt;code&gt;Vote&lt;/code&gt; class's code itself though, reveals a neater option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nf"&gt;loadVote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;voteData&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Encountered an error, returning placeholder data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;votes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;yes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;no&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;someRedisClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadVote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;incrementCounterAndSave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt; 
&lt;span class="c1"&gt;// Encountered an error, returning placeholder data: &lt;/span&gt;
&lt;span class="c1"&gt;//   'Unable to find result for someId (error here)&lt;/span&gt;
&lt;span class="c1"&gt;// { votes: { yes: [], no: [] } }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I figured I'd write this up since, while I've worked with Promises for a while now, this concept wasn't my first instinct: I didn't think to use &lt;code&gt;catch&lt;/code&gt; anywhere but at the very end of my chain of &lt;code&gt;then&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;Here's some code you can play around with in the console that demonstrates this pretty simply!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataFetcher&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Cause this Promise to be rejected/fail every other time the fetch function is called.&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data from DB!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataFetcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;placeholder data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// placeholder data&lt;/span&gt;
&lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//data from DB!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a side note, you could totally write this code in a less nested (and arguably more readable) way using &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; - I don't feel super strongly about it either way, so I just went with Promises.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>promises</category>
      <category>slackbots</category>
    </item>
  </channel>
</rss>
