<?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: Alberto Guerra González</title>
    <description>The latest articles on DEV Community by Alberto Guerra González (@albertowar).</description>
    <link>https://dev.to/albertowar</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%2F40038%2Fcd35837d-87e9-40b7-b74f-3a8a6f872347.png</url>
      <title>DEV Community: Alberto Guerra González</title>
      <link>https://dev.to/albertowar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/albertowar"/>
    <language>en</language>
    <item>
      <title>Can't cache this 🎶</title>
      <dc:creator>Alberto Guerra González</dc:creator>
      <pubDate>Mon, 06 Dec 2021 16:15:45 +0000</pubDate>
      <link>https://dev.to/albertowar/cant-cache-this-5925</link>
      <guid>https://dev.to/albertowar/cant-cache-this-5925</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We often see companies publishing a short incident recap after they had a major outage. A couple of recent ones were &lt;a href="https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; and &lt;a href="https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Although knowing the actual root cause is interesting, I think there is a lot to learn from how the engineers managed to troubleshoot the issue.&lt;/p&gt;

&lt;p&gt;So I decided to take a stab at it and walk you through the troubleshooting process that took place in one of our recent incidents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The beginning of it all
&lt;/h2&gt;

&lt;p&gt;A few weeks ago, my team was preparing for the launch of an alpha version of a new product. Engineers were heads down wrapping up the last features and smashing bugs when our QA raised a bug in production.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fa6riGOBa6s"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The users should have been redirected to the payment page. However, quite often, the underlying request would timeout when anyone was attempting to click on the Buy button. &lt;/p&gt;

&lt;p&gt;This was a regression and a major one, hence we decided to jump on it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/nrXif9YExO9EI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/nrXif9YExO9EI/giphy.gif" alt="Fire"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigation
&lt;/h2&gt;

&lt;p&gt;The first step was to dive into the &lt;strong&gt;backend app logs&lt;/strong&gt;. Here is what we saw:&lt;/p&gt;

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

&lt;p&gt;Judging by the logs, it looked like the request &lt;strong&gt;froze&lt;/strong&gt; 🧊 at some point during the execution path. This was hard to believe since the app is simple enough that shouldn't lead to any deadlocks. However, we should have seen an &lt;code&gt;Exception&lt;/code&gt; somewhere and that wasn't the case here.&lt;/p&gt;

&lt;p&gt;Also, what made it even more interesting was the fact that we also saw some requests that executed normally.&lt;/p&gt;

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

&lt;p&gt;Nevertheless, since the bug was fairly easy to reproduce, we needed to keep digging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diving into the code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/xiEUIhXadorPr9tait/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xiEUIhXadorPr9tait/giphy.gif" alt="Dive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step in the investigation was to analyze the log lines in the code and pin point 🎯 the section that was causing the request thread to freeze. This can simply be done by &lt;strong&gt;looking at the last log line reported and the next log line that should have been reported&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This exercise brought to light the first suspect. As part of the request, we &lt;strong&gt;fetched a large JSON file from an external service&lt;/strong&gt;. Since this file didn't change very often, we added &lt;strong&gt;a caching layer&lt;/strong&gt; to save some network requests and reduce the latency of the request.&lt;/p&gt;

&lt;p&gt;To implement this caching layer, we decided to use a &lt;a href="https://github.com/google/guava" rel="noopener noreferrer"&gt;Guava&lt;/a&gt; cache. The cache was configured so that it would return old values while the new values were being fetched.&lt;/p&gt;

&lt;p&gt;Here is a fragment of code showing how the cache was configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refreshAfterWrite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CACHE_REFRESH_TIME_MS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MILLISECONDS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buildCacheLoader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;CacheLoader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;buildCacheLoader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&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;CacheLoader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nd"&gt;@Override&lt;/span&gt;
      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fetching data"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;HttpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;connectTimeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIMEOUT_IN_SECONDS&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nc"&gt;HttpRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIMEOUT_IN_SECONDS&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BodyHandlers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At this point, it was clear that the cache wasn't behaving as we expected since:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It wasn't logging the potential &lt;code&gt;Exception&lt;/code&gt; that would explain what was causing the request thread to hang.&lt;/li&gt;
&lt;li&gt;It was causing the request thread to hang.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thus, I decided to spend some time trying to understand in depth how Guava caches work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up the playground
&lt;/h2&gt;

&lt;p&gt;In situations like this one, I usually create a playground project where I can remove all the noise (business logic) and focus on what matters 🧐.&lt;/p&gt;

&lt;p&gt;I had to come up with a setup as close to production to ensure I was able to reproduce the exact same issue. &lt;/p&gt;

&lt;p&gt;To achieve that, I decided to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interact with the cache in &lt;strong&gt;separate threads&lt;/strong&gt;. Since the web server framework we rely on executes each request on a different thread (within a thread pool).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;WireMock&lt;/strong&gt; to simulate the calls to the external service. That way we can make sure the network stack is exercised and that it is easy to set up edge case conditions (delays, error responses, etc).&lt;/li&gt;
&lt;li&gt;Use JUnit to setup the different scenarios and run them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ensure we have visibility in everything that happens during the different test cases, the threads will run the following Runnable which includes several log lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheReader&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Runnable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CacheReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@Override&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reading value at {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value {} at {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"something"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExecutionException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exception while loading value at {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ❓ Mystery 1 - Exception MIA
&lt;/h3&gt;

&lt;p&gt;The first mystery to solve was the missing &lt;code&gt;Exception&lt;/code&gt;. For that, there are two potential failures we could have experienced in production that should have reported an &lt;code&gt;Exception&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The cache failed to be populated on the first attempt.&lt;/li&gt;
&lt;li&gt;The cache was populated but an attempt to refresh the cache later on failed (either due to an error on the external service or a network timeout).&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  🧪 Test 1 - Given an empty cache and a slow network connection, when fetching the cache, it should throw an Exception
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="nd"&gt;@SneakyThrows&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;givenEmptyCache_whenRemoteServiceTimeout_shouldThrowException&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildCacheWithoutExceptionHandling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wireMockServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;setupRemoteResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&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="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIMEOUT_IN_SECONDS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;thread&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;Thread&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;CacheReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which reports the following logs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1861 [Thread-8] INFO  org.example.CacheReader [] - Reading value at 1637941200937
1872 [Thread-8] INFO  org.example.CacheFactory [] - Fetching data
2925 [Thread-8] ERROR org.example.CacheReader [] - Exception while loading value at 1637941202006
java.util.concurrent.ExecutionException: java.net.http.HttpTimeoutException: request timed out 
... &amp;lt;omitted stacktrace&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When the cache is empty, the thread loading the cache seems to be the one that receives the &lt;code&gt;Exception&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we didn't see any &lt;code&gt;Exception&lt;/code&gt; in our logs, this means this wasn't the failure we observed in production. Let's keep going.&lt;/p&gt;
&lt;h4&gt;
  
  
  🧪 Test 2 - Given an empty cache and a external service outage, when fetching the cache, it should throw an &lt;code&gt;Exception&lt;/code&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;givenEmptyCache_whenRemoteServiceReturns500_shouldThrowException&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="nc"&gt;CacheFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildCacheWithoutExceptionHandling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wireMockServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;setupFailedResponse&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;thread&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;Thread&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;CacheReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which reports the following logs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1793 [Thread-8] INFO  org.example.CacheReader [] - Reading value at 1637944243058
1807 [Thread-8] INFO  org.example.CacheFactory [] - Fetching data
2070 [Thread-8] ERROR org.example.CacheReader [] - Exception while loading value at 1637944243341
java.util.concurrent.ExecutionException: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
 at [Source: (String)""; line: 1, column: 0]
... &amp;lt;omitted stacktrace&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this case, the calling thread is getting a serialization &lt;code&gt;Exception&lt;/code&gt; since the response body was not the expected one.&lt;/p&gt;

&lt;p&gt;Again, since we didn't see any &lt;code&gt;Exception&lt;/code&gt; in our logs, we need to continue the investigation.&lt;/p&gt;
&lt;h4&gt;
  
  
  🧪 Test 3 - Given a populated cache due for refresh and a slow network connection, when fetching the cache, it should get the old value
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="nd"&gt;@SneakyThrows&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;givenCacheDueForRefresh_whenRemoteServiceTimeout_shouldGetOldValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildCacheWithoutExceptionHandling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wireMockServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;setupRemoteResponse&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;Data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"old_value"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;thread&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;Thread&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;CacheReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;waitForCacheToExpire&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;setupRemoteResponse&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;Data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"new_value"&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="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIMEOUT_IN_SECONDS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;thread&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;Thread&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;CacheReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which reports the following logs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1834 [Thread-8] INFO  org.example.CacheReader [] - Reading value at 1637945326283
1846 [Thread-8] INFO  org.example.CacheFactory [] - Fetching data
2099 [Thread-8] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945326554
3114 [Thread-9] INFO  org.example.CacheReader [] - Reading value at 1637945327569
3115 [Thread-9] INFO  org.example.CacheFactory [] - Reloading
3115 [Thread-9] INFO  org.example.CacheFactory [] - Fetching datacom.google.common.cache.LocalCache$Segment$1 run
WARNING: Exception thrown during refresh
java.util.concurrent.ExecutionException: java.net.http.HttpTimeoutException: request timed out
    at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:566)
4296 [Thread-9] INFO  org.example.CacheReader [] - Value old_value at 1637941240139
    at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:527)
... &amp;lt;omitted stacktrace&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In these logs, we see that the thread that attempts to load the value after the cache is due for refresh &lt;strong&gt;will block&lt;/strong&gt; (we will follow up on this later) in attempt to reload the new value. However, if that fails, it will get the old value and an Exception will be logged in the background.&lt;/p&gt;

&lt;p&gt;When we look into the &lt;a href="https://github.com/google/guava/wiki/CachesExplained#refresh" rel="noopener noreferrer"&gt;Guava documentation&lt;/a&gt; for the type of cache we are using (&lt;code&gt;refreshAfterWrite&lt;/code&gt;), we find the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If an exception is thrown while refreshing, the old value is kept, and the exception is logged and swallowed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This explains why we never saw the Exception in the logs. It was logged by Guava and shallowed which was being filtered out when our app sends those logs to NewRelic. This was a decision we made to avoid polluting NewRelic with logs that we don't care about but it ended up biting us in the arse 🤦&lt;/p&gt;

&lt;p&gt;Mystery 1 solved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/11ISwbgCxEzMyY/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/11ISwbgCxEzMyY/giphy.gif" alt="Good"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  ❓ Mystery 2 - Request thread hanging
&lt;/h3&gt;

&lt;p&gt;While troubleshooting the previous issue, we found that, when the cache is due for refresh, the thread that attempts to fetch the new value will block and, if it fails, it will eventually get the old value.&lt;/p&gt;

&lt;p&gt;The next thing to figure out is what happens if many theads attempt to get the value from the cache at the same time when it is due for refresh&lt;/p&gt;
&lt;h4&gt;
  
  
  🧪 Test 4 - Given a populated cache due for refresh and a slow network connection, when many threads attempt to fetch the cache, they should all get the old value.
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="nd"&gt;@SneakyThrows&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;givenCacheDueForRefresh_whenRemoteServiceTimeout_allThreadsShouldGetOldValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildCacheWithoutExceptionHandling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wireMockServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;setupRemoteResponse&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;Data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"old_value"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;thread&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;Thread&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;CacheReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;waitForCacheToExpire&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;setupRemoteResponse&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;Data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"new_value"&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="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIMEOUT_IN_SECONDS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;runManyAndWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which reports the following logs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1861 [Thread-8] INFO  org.example.CacheReader [] - Reading value at 1637945351638
1871 [Thread-8] INFO  org.example.CacheFactory [] - Fetching data
2118 [Thread-8] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945351899
3132 [Thread-11] INFO  org.example.CacheReader [] - Reading value at 1637945352913
3132 [Thread-10] INFO  org.example.CacheReader [] - Reading value at 1637945352913
3132 [Thread-9] INFO  org.example.CacheReader [] - Reading value at 1637945352913
3132 [Thread-9] INFO  org.example.CacheFactory [] - Reloading
3132 [Thread-10] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945352913
3132 [Thread-9] INFO  org.example.CacheFactory [] - Fetching data
3132 [Thread-11] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945352913com.google.common.cache.LocalCache$Segment$1 run
4185 [Thread-9] INFO  org.example.CacheReader [] - Value old_value at 1637942442820
WARNING: Exception thrown during refresh
java.util.concurrent.ExecutionException: java.net.http.HttpTimeoutException: request timed out
... &amp;lt;omitted stacktrace&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, we observe that the unlucky &lt;code&gt;Thread-9&lt;/code&gt; blocks to fetch the new value while all the other threads get the old value and move on with their lives.&lt;/p&gt;

&lt;p&gt;This gives us enough information to conclude what happened in production!&lt;/p&gt;

&lt;p&gt;When our QA was running their regression test, their requests would often be the ran by an unlucky thread that gets blocked while fetching the new value. Since we have multiple instances of this application, this could be reproduced easily. &lt;/p&gt;

&lt;p&gt;Funny enough, when multiple people were trying at the same time, if they were lucky enough to hit the same instance, everything would work just fine, which is what we saw in the logs.&lt;/p&gt;

&lt;p&gt;Mystery 2 solved!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3rUbeDiLFMtAOIBErf/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3rUbeDiLFMtAOIBErf/giphy.gif" alt="Success"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Are we done?
&lt;/h2&gt;

&lt;p&gt;Not quite!&lt;/p&gt;

&lt;p&gt;We have learnt that, when the cache is due for refresh, there is always an unlucky thread that has to pay the latency cost of refreshing the value.&lt;/p&gt;

&lt;p&gt;To provide the best experience for our users, we should make sure the cache refresh takes place asynchronously and that all requests get the old value while the new one is being fetched.&lt;/p&gt;

&lt;p&gt;So ... how do we do that? 🤔&lt;/p&gt;
&lt;h3&gt;
  
  
  Actually fixing the problem
&lt;/h3&gt;

&lt;p&gt;If we look again into &lt;a href="https://github.com/google/guava/wiki/CachesExplained#refresh" rel="noopener noreferrer"&gt;Guava's documentation&lt;/a&gt; we can see a couple of comments about asynchronous reload:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...] As specified in LoadingCache.refresh(K), refreshing a key loads a new value for the key, &lt;strong&gt;possibly asynchronously&lt;/strong&gt; [...]&lt;/p&gt;

&lt;p&gt;[...] If CacheLoader.reload is implemented to be asynchronous, then the query will not be slowed down by the refresh [...]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately, they forgot to add an example on how to do it! 😅&lt;/p&gt;

&lt;p&gt;It's alright, 💩 happens. A couple of DuckDuckGo searches later, we found that we could achieve this by configuring the cache to use &lt;code&gt;asyncReloading&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;buildCacheWithAsyncLoading&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;CacheBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refreshAfterWrite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CACHE_REFRESH_TIME_MS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MILLISECONDS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CacheLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asyncReloading&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CacheFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildCacheLoader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this case, the refresh will take place on a thread from a separate thread pool we have to pass on during instantiation.&lt;/p&gt;

&lt;p&gt;To validate this works as expected, we write another test 🧪:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="nd"&gt;@SneakyThrows&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;solution_whenFutureResponsesTimeoutAndAsyncLoading_returnOldValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CacheFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildCacheWithAsyncLoading&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wireMockServer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;setupRemoteResponse&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;Data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"old_value"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

      &lt;span class="n"&gt;runAndWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;waitForCacheToExpire&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

      &lt;span class="n"&gt;setupRemoteResponse&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;Data&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"new_value"&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="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TIMEOUT_IN_SECONDS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;runManyAndWait&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;THREAD_COUNT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which reports the following logs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1772 [Thread-8] INFO  org.example.CacheReader [] - Reading value at 1637945095606
1783 [Thread-8] INFO  org.example.CacheFactory [] - Fetching data
2032 [Thread-8] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945095871
3049 [Thread-9] INFO  org.example.CacheReader [] - Reading value at 1637945096888
3049 [Thread-10] INFO  org.example.CacheReader [] - Reading value at 1637945096888
3050 [Thread-11] INFO  org.example.CacheReader [] - Reading value at 1637945096889
3050 [Thread-11] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945096889
3050 [Thread-10] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945096889
3053 [pool-2-thread-1] INFO  org.example.CacheFactory [] - Reloading
3053 [pool-2-thread-1] INFO  org.example.CacheFactory [] - Fetching data
3065 [Thread-9] INFO  org.example.CacheReader [] - Value Data(value=old_value) at 1637945096904
4063 [pool-2-thread-1] ERROR org.example.CacheFactory [] - Loading Exception at 1637945097902
... &amp;lt;omitted stacktrace&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can see that there isn't an unlucky thread anymore and that the &lt;code&gt;Exception&lt;/code&gt; is reported by the thread in the pool.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When we encounter an issue in production, depending on its severity and impact, we often perform an RCA (root cause analysis) investigation.&lt;/p&gt;

&lt;p&gt;The goal of this process is to find the root cause of the problem, fix it and also to come up with ways we can prevent issues like this happening in the future or, at least, improve our automated monitoring to ensure we catch these issues as soon as they happen.&lt;/p&gt;

&lt;p&gt;For this particular problem, apart from fixing the bug, we decided to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;NewRelic scripted browser synthetics&lt;/strong&gt; to periodically run a smoke test similar to what QA was doing as part of the regression test. This will allow us to catch user-impacting issues early.&lt;/li&gt;
&lt;li&gt;Tweak our request &lt;strong&gt;latency alerts&lt;/strong&gt;. With a similar goal as the one above.&lt;/li&gt;
&lt;li&gt;Come up with a plan to &lt;strong&gt;implement browser-based E2E automation&lt;/strong&gt;. This will allow us to catch regressions in internal environments before they reach production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's the end of it all!&lt;/p&gt;

&lt;p&gt;If you are curious to see the playground code, you can find it in &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AnotherDevBoy" rel="noopener noreferrer"&gt;
        AnotherDevBoy
      &lt;/a&gt; / &lt;a href="https://github.com/AnotherDevBoy/guava-playground" rel="noopener noreferrer"&gt;
        guava-playground
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;This repository contains some tests that I used to investigate a tricky caching bug.&lt;/p&gt;
&lt;p&gt;You can find more information about it in my blog &lt;a href="https://albertowar.me/" rel="nofollow noopener noreferrer"&gt;albertowar.me&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AnotherDevBoy/guava-playground" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>java</category>
      <category>rca</category>
      <category>troubleshooting</category>
      <category>oncall</category>
    </item>
    <item>
      <title>Name your threads</title>
      <dc:creator>Alberto Guerra González</dc:creator>
      <pubDate>Sat, 27 Nov 2021 21:08:27 +0000</pubDate>
      <link>https://dev.to/albertowar/name-your-threads-2bdf</link>
      <guid>https://dev.to/albertowar/name-your-threads-2bdf</guid>
      <description>&lt;p&gt;A few weeks ago, my team decided to start leveraging &lt;a href="https://docs.newrelic.com/docs/apm/agents/java-agent/features/real-time-profiling-java-using-jfr-metrics/" rel="noopener noreferrer"&gt;NewRelic JFR&lt;/a&gt; which is a relatively new feature from NewRelic to provide visibility on &lt;a href="https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm#JFRUH170" rel="noopener noreferrer"&gt;JFR&lt;/a&gt; events, and I noticed something that got me thinking 🤔&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%2Fpyp19cbpdn82j9ebglxl.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%2Fpyp19cbpdn82j9ebglxl.png" alt=" " width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Who the hell is creating all these threads?&lt;/p&gt;

&lt;p&gt;By the way, you don't need to use NewRelic or JFR to have a look at your threads. Since JVM threads are actually OS threads, you can also view them with &lt;code&gt;ps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Simply, type &lt;code&gt;ps -ef&lt;/code&gt; to find the ID of your java process and then &lt;code&gt;ps -T -p &amp;lt;pid&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my case, it gave me the following output (redacted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  PID  SPID TTY          TIME CMD
    2     2 ?        00:00:00 java
    2     3 ?        00:00:08 java
    2     4 ?        00:10:17 GC Thread#0
                     ...
    2    17 ?        00:10:17 GC Thread#1
    2    34 ?        00:00:01 New Relic Jar A
                     ...
    2    50 ?        00:00:02 pool-3-thread-1
    2    51 ?        00:00:02 redisson-netty-
                     ...
    2    89 ?        00:00:39 pool-5-thread-1
                     ...
    2   109 ?        01:16:09 pool-6-thread-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Threads everywhere
&lt;/h2&gt;

&lt;p&gt;As our applications grow larger, we start making use of libraries to make external web requests, database queries, etc. Often times, these libraries make use of threads (or thread pools) behind the scenes to improve their throughput. &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%2Fwvxkp9x82fhkux04ln2l.gif" 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%2Fwvxkp9x82fhkux04ln2l.gif" alt="Everywhere" width="498" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The JVM assigns threads some default names like the ones we observed above (&lt;code&gt;Thread-%&lt;/code&gt;, &lt;code&gt;pool-%-thread-%&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Although these names make sense as default values, they are not as helpful when we try to understand if a particular set of threads is misbehaving. &lt;/p&gt;

&lt;p&gt;This could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocating too much memory. &lt;/li&gt;
&lt;li&gt;Taking too much CPU time. &lt;/li&gt;
&lt;li&gt;Spinning up too many threads (which also has a memory usage impact).&lt;/li&gt;
&lt;li&gt;Deadlocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Name your pets
&lt;/h2&gt;

&lt;p&gt;Mike Wazowski once said: &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%2F8bm4cjtfm0hvt53g3g0b.gif" 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%2F8bm4cjtfm0hvt53g3g0b.gif" alt="Because once you name it, you get attached to it" width="244" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, I would recommend not to follow his advice and name your 🧵 &lt;/p&gt;

&lt;p&gt;This will save you time in the future since you will be able to use effectively many tools that will help you troubleshooting any performance issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I do that?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Threads
&lt;/h3&gt;

&lt;p&gt;If you are the one creating the thread, you can simply do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"boo"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pools
&lt;/h3&gt;

&lt;p&gt;If you are creating the thread pool, the &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html" rel="noopener noreferrer"&gt;&lt;code&gt;Executors&lt;/code&gt;&lt;/a&gt; class allows you to pass a &lt;code&gt;ThreadFactory&lt;/code&gt; to its different factory methods.&lt;/p&gt;

&lt;p&gt;If you combine that with &lt;a href="https://github.com/google/guava" rel="noopener noreferrer"&gt;Guava's&lt;/a&gt; &lt;a href="https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ThreadFactoryBuilder.html" rel="noopener noreferrer"&gt;&lt;code&gt;ThreadFactoryBuilder&lt;/code&gt;&lt;/a&gt;, you can easily make sure your threads are named with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ThreadFactoryBuilder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setNameFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"boo-%d"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&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;factory&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;Once &lt;a href="https://openjdk.java.net/projects/loom/" rel="noopener noreferrer"&gt;Project Loom&lt;/a&gt; lands on the SDK, things may change and this advice may not be as useful, since there will no longer be a 1-1 mapping between OS threads and JVM virtual threads.&lt;/p&gt;

&lt;p&gt;However, that will take some time to be widely available and even more so to an LTS version (next LTS is planned for &lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html" rel="noopener noreferrer"&gt;September 2023&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To end, I will go against the pet vs cattle DevOps's mindset and say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat your threads as pets, not cattle ... for now&lt;/p&gt;
&lt;/blockquote&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%2Firh3d3iunxmuzj7jqrjk.gif" 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%2Firh3d3iunxmuzj7jqrjk.gif" alt="Gru kissing minions" width="400" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>threads</category>
      <category>troubleshooting</category>
      <category>devops</category>
    </item>
    <item>
      <title>Mob programming revisited</title>
      <dc:creator>Alberto Guerra González</dc:creator>
      <pubDate>Sun, 02 May 2021 19:58:44 +0000</pubDate>
      <link>https://dev.to/albertowar/mob-programming-revisited-2fo4</link>
      <guid>https://dev.to/albertowar/mob-programming-revisited-2fo4</guid>
      <description>&lt;p&gt;At the beginning of the year, I wrote a post about the &lt;a href="https://dev.to/albertowar/lessons-learnt-from-my-first-mob-programming-session-5hjf"&gt;first mob programming session&lt;/a&gt; that I organized with my team.&lt;/p&gt;

&lt;p&gt;The results were promising, yet we never had a chance to use it in real world examples.&lt;/p&gt;

&lt;p&gt;That changed last week.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background story
&lt;/h1&gt;

&lt;p&gt;For the last 6 months, my team has worked on a project that had a very optimistic deadline from day one.&lt;/p&gt;

&lt;p&gt;We had to take many shortcuts in order to make it and one of these shortcuts was &lt;strong&gt;siloing&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In order to ship the required features on time, we distributed the ownership of the different parts of the application across the team and worked on them &lt;strong&gt;separately&lt;/strong&gt;.&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%2Fwsudtc0w76vsstrgm0ko.gif" 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%2Fwsudtc0w76vsstrgm0ko.gif" alt="Isolated" width="400" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  💩 Shit storm
&lt;/h1&gt;

&lt;p&gt;Fast forward to a week ago, we were in a tricky situation. We had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One component in our application that &lt;strong&gt;only 1 developer&lt;/strong&gt; knew how it worked.&lt;/li&gt;
&lt;li&gt;A bunch of &lt;strong&gt;new features&lt;/strong&gt; lined up in our roadmap that we committed to deliver by the &lt;strong&gt;end of May&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The need to perform a &lt;strong&gt;significant refactor&lt;/strong&gt; on that component to prepare for those features.&lt;/li&gt;
&lt;li&gt;The main SME was off &lt;strong&gt;sick&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&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%2Fsqttxi7mdetvpvmum1rb.gif" 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%2Fsqttxi7mdetvpvmum1rb.gif" alt="Chaos" width="240" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Mob programming to the rescue!
&lt;/h1&gt;

&lt;p&gt;The situation was less of ideal. This was the result of team debt accumulated over time, and it was time to pay it off.&lt;/p&gt;

&lt;p&gt;I brought up the idea of using mob programming for the refactoring work. In my mind, this would improve the &lt;a href="https://en.wikipedia.org/wiki/Bus_factor" rel="noopener noreferrer"&gt;bus factor&lt;/a&gt; since:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All engineers would work &lt;strong&gt;together&lt;/strong&gt; on the same task at the same time.&lt;/li&gt;
&lt;li&gt;Refactoring forces you to &lt;strong&gt;understand&lt;/strong&gt; the code &lt;strong&gt;before changing&lt;/strong&gt; it to ensure it still behaves in the same way after the changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gladly management bought onto the idea and we spent a full week working on it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Retrospective
&lt;/h1&gt;

&lt;h2&gt;
  
  
  💻 Preparation
&lt;/h2&gt;

&lt;p&gt;In order to save valuable time, make sure you share instructions on &lt;strong&gt;how to set up the development environment&lt;/strong&gt; prior to the session.&lt;/p&gt;

&lt;p&gt;I forgot to do it, therefore we had to repeat the same steps every time we rotated to a new driver 🤦‍♂️.&lt;/p&gt;

&lt;h2&gt;
  
  
  ➡️ One direction
&lt;/h2&gt;

&lt;p&gt;It is important that all navigators follow the same &lt;strong&gt;train of thought&lt;/strong&gt;. &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%2F3z2tkv9cjbjvqn6o7lm6.gif" 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%2F3z2tkv9cjbjvqn6o7lm6.gif" alt="Together" width="224" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's hard follow the conversation when different people are trying to tackle different parts of the problem, specially if you are the driver.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Facilitation is key
&lt;/h2&gt;

&lt;p&gt;I found it really hard to contribute to the session and facilitate it at the same time. However, having a facilitator is critical to ensure you make the most of your time.&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%2Fdkhbo4i9qaxj2d285j0z.gif" 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%2Fdkhbo4i9qaxj2d285j0z.gif" alt="Meditation" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The facilitator will be in charge of the following.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 1. Defining a goal for the session
&lt;/h3&gt;

&lt;p&gt;At the beginning of the session, the facilitator should &lt;strong&gt;state&lt;/strong&gt; what you want to &lt;strong&gt;achieve&lt;/strong&gt; by the end of the &lt;strong&gt;day&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This will help to prioritize the areas of focus and when it is OK to move on.&lt;/p&gt;

&lt;h3&gt;
  
  
  ☮️ 2. Creating a safe environment
&lt;/h3&gt;

&lt;p&gt;As a facilitator, you need to ensure everyone in the room has an &lt;strong&gt;opportunity to share their ideas&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In every team, there will be people with stronger voices in the room. As a facilitator, you can intervene with prompts like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Hey X (person who has been silent), what are your thoughts on Y (the problem at hand)?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Excuse me X (person who's speaking over somebody else), I would like to hear what Y has to say, I think they have a valid point.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Occasionally, people may get hung up on some details, react sarcastically, make negative remarks about the code that's been written or the driver.&lt;/p&gt;

&lt;p&gt;This can be quite &lt;strong&gt;distracting&lt;/strong&gt; and some people might &lt;strong&gt;not feel safe&lt;/strong&gt; sharing their ideas in such an environment. &lt;/p&gt;

&lt;p&gt;If you, as the facilitator, observe this is happening, you should also intercede in a respectful way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Apologies for interrupting, I would like to kindly ask that we leave sarcasm or non-constructive feedback outside of this session. It is distracting some members of the mob and we all want to get the most out of this session.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, to avoid an awkward silence, you can quickly redirect the attention back to code:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;So, Y (navigator), how do you think we should solve X (problem at hand)?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🛣️ 3. Staying on the right track
&lt;/h3&gt;

&lt;p&gt;Although one of the main values of mob programming is the discussion that takes place while multiple people are working on the same piece of code, it is also important to ensure the mob doesn't get &lt;strong&gt;stuck&lt;/strong&gt; with things that are not relevant.&lt;/p&gt;

&lt;p&gt;As developers, we sometimes get hung up with details such as variable names, libraries to use, etc.&lt;/p&gt;

&lt;p&gt;If this happens, the facilitator should intervene and bring the session back on track. Here are some ways you could do it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Hey folks! I think you all have valid points. Also, we have been discussing this point for a while now, can we compromise on one solution and move on to the next problem?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It seems this topic needs more discussion. Should we keep track of this item and come back to it at a separate meeting?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  📝 4. Track decisions
&lt;/h3&gt;

&lt;p&gt;During the session, the mob will make &lt;strong&gt;decision&lt;/strong&gt; that impacts the feature or product that's been developed. &lt;/p&gt;

&lt;p&gt;This could be anything from the programming language to use, structure of the code, picking a library to solve a problem, the architecture of the system ...&lt;/p&gt;

&lt;p&gt;When that happens, the facilitator should make sure that such a &lt;strong&gt;decision&lt;/strong&gt; and the reason behind it are &lt;strong&gt;tracked&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;For this, you could use an &lt;a href="https://adr.github.io/" rel="noopener noreferrer"&gt;architectural decision record&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ℹ️ 5. Progress updates
&lt;/h3&gt;

&lt;p&gt;Mob programming is still a fairly new way to solve problems and many people are still unfamiliar with its value.&lt;/p&gt;

&lt;p&gt;An easy way to keep everyone informed is to send a &lt;strong&gt;recap&lt;/strong&gt; at the end of the mail. &lt;/p&gt;

&lt;p&gt;Here, you can share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the mob &lt;strong&gt;accomplished&lt;/strong&gt; during the session.&lt;/li&gt;
&lt;li&gt;What is still left &lt;strong&gt;to do&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Any &lt;strong&gt;questions or potential blockers&lt;/strong&gt; that might have popped up during the session and need to be &lt;strong&gt;addressed separately&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&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%2Fhaste.net%2Fwp-content%2Fuploads%2F2017%2F08%2Fhaste-league-of-legends-victory.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%2Fhaste.net%2Fwp-content%2Fuploads%2F2017%2F08%2Fhaste-league-of-legends-victory.png" alt="Victory" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having a practical example where we could apply this way of solving problems was quite illustrative.&lt;/p&gt;

&lt;p&gt;Not only we &lt;strong&gt;achieved&lt;/strong&gt; our goal of &lt;strong&gt;sharing knowledge&lt;/strong&gt; across other engineers, but we also &lt;strong&gt;learnt&lt;/strong&gt; more about mob programming and what &lt;strong&gt;type of problems&lt;/strong&gt; are best suited for it.&lt;/p&gt;

&lt;p&gt;With that said, there are still some details that aren't quite clear to us yet. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should the driver also be part of the discussion? &lt;/li&gt;
&lt;li&gt;How often should we rotate?&lt;/li&gt;
&lt;li&gt;Should we involve people from other disciplines?&lt;/li&gt;
&lt;li&gt;What's the optimal number of engineers?&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Next steps
&lt;/h1&gt;

&lt;p&gt;To continue discovering more about mob programming, I plan to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;👀 Continue looking for &lt;strong&gt;more opportunities&lt;/strong&gt; to put it into practice at work.&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Read&lt;/strong&gt; &lt;a href="https://www.mobprogrammingguidebook.com/images/mobprogrammingguidebook.pdf" rel="noopener noreferrer"&gt;The Mob Programming Guidebook&lt;/a&gt; and &lt;a href="https://www.amazon.com/dp/B081F5FVTZ" rel="noopener noreferrer"&gt;Remote Mob Programming&lt;/a&gt; to see if I can find answers for the questions above.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>mobprogramming</category>
      <category>pairprogramming</category>
      <category>productivity</category>
      <category>teamwork</category>
    </item>
    <item>
      <title>Setting goals in the middle of a pandemic</title>
      <dc:creator>Alberto Guerra González</dc:creator>
      <pubDate>Mon, 11 Jan 2021 20:52:34 +0000</pubDate>
      <link>https://dev.to/albertowar/setting-goals-in-the-middle-of-a-pandemic-3b2a</link>
      <guid>https://dev.to/albertowar/setting-goals-in-the-middle-of-a-pandemic-3b2a</guid>
      <description>&lt;p&gt;In &lt;strong&gt;&lt;a href="https://dev.to/albertowar/2020-goals-in-review-5abj"&gt;a previous post&lt;/a&gt;&lt;/strong&gt;, I looked back at my &lt;strong&gt;2020 goals&lt;/strong&gt; and what I managed to achieve by the end of the year. &lt;/p&gt;

&lt;p&gt;I have been going through that exercise for a few years now. Nonetheless, 2020 taught me a lesson when it comes to goal setting in a volatile context and I wanted to share that with you.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting goals
&lt;/h1&gt;

&lt;p&gt;Before I get to my recommendations for goal setting in the middle of a pandemic, first I will describe my approach to setting goals in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Areas of improvement
&lt;/h2&gt;

&lt;p&gt;I always start the process defining areas that I want to improve on during the year. These could be your &lt;em&gt;finances, health, relationships, work, etc.&lt;/em&gt;&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%2F6tx9zlct92qkifgthm2z.gif" 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%2F6tx9zlct92qkifgthm2z.gif" alt="Choose area" width="480" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can choose as many as you want with the caveat that, the more areas you choose the less focused you will be. This means you will make some progress in every area but you won't be able to make big changes.&lt;/p&gt;

&lt;p&gt;If this is the first time you are setting goals, I would suggest you start small. Pick &lt;strong&gt;one or two areas&lt;/strong&gt; where you can make a &lt;strong&gt;big impact.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - GROW
&lt;/h2&gt;

&lt;p&gt;Once you have picked your areas of focus, the next step would be to define your goals and how you will achieve them.&lt;/p&gt;

&lt;p&gt;For this, I usually follow the &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/GROW_model" rel="noopener noreferrer"&gt;GROW model&lt;/a&gt;.&lt;/strong&gt; This framework has been used in coaching for many years and has proven to be fairly successful.&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%2F95va8t4j31y9xgoj2grh.gif" 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%2F95va8t4j31y9xgoj2grh.gif" alt="Grow" width="500" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;G&lt;/strong&gt;oal 🏁
&lt;/h3&gt;

&lt;p&gt;Start by defining &lt;strong&gt;what&lt;/strong&gt; it is that you want to achieve.&lt;/p&gt;

&lt;p&gt;Coming up with "good" goals can be challenging. In the next section, I will share more details on how to do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;R&lt;/strong&gt;eality 🌍
&lt;/h3&gt;

&lt;p&gt;Before you can elaborate a path to achieve your goal, you need to understand &lt;strong&gt;where you are now&lt;/strong&gt; towards that goal. This will help you assess how much time you will actually need to meet the goal.&lt;/p&gt;

&lt;p&gt;It might be useful to find a buddy that can give you their &lt;em&gt;objective point of view&lt;/em&gt; in the matter. For career development, your manager or someone more senior would be an ideal candidate for this. For personal growth, a friend or a partner can also help.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;O&lt;/strong&gt;ptions ⚖️
&lt;/h3&gt;

&lt;p&gt;Now that you know where you want to get and your starting point, it is the time to brainstorm ideas on &lt;strong&gt;how you can get there.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most of the time, there are multiple paths you can take. This is your opportunity to evaluate their pros/cons so that you can make an informed decision. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;W&lt;/strong&gt;ay forward 🛣️
&lt;/h3&gt;

&lt;p&gt;Finally, it is the time for you to narrow down the options and &lt;strong&gt;choose the best one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Depending on the scope of your goal or the amount of time you have to complete it, you might need to more than one action item from the list you came up with in the previous step.&lt;/p&gt;

&lt;p&gt;     As an example, imagine your goal (&lt;strong&gt;G&lt;/strong&gt;) is to lose weight by the end of the year. In particular, 10 kg. You current weight is 100 kg (&lt;strong&gt;R&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;     There a few ways you can tackle this goal (&lt;strong&gt;O&lt;/strong&gt;): exercise, changing your diet, going on caloric deficit, etc.&lt;/p&gt;

&lt;p&gt;     Since you enjoy playing tennis, you decide to join a club and play 3 times a week (&lt;strong&gt;W&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;If you want to learn more about coaching or the GROW model, I recommend you read &lt;strong&gt;&lt;a href="https://amzn.to/35oomMf" rel="noopener noreferrer"&gt;Coaching for Performance&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SMART goals
&lt;/h2&gt;

&lt;p&gt;A nuance I introduce as part of the GROW model is the use of the &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/SMART_criteria" rel="noopener noreferrer"&gt;SMART criteria&lt;/a&gt;&lt;/strong&gt; when it comes to defining goals.&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%2Fgdvoovussjewdz1i5ib3.gif" 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%2Fgdvoovussjewdz1i5ib3.gif" alt="Smart" width="480" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;S&lt;/strong&gt;pecific 🎯
&lt;/h3&gt;

&lt;p&gt;Your goal should be specific. Otherwise, it will be hard to know whether you &lt;strong&gt;achieved it or not&lt;/strong&gt;. In the previous example, we set 10 kg as a weight loss goal which is easy to track.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;M&lt;/strong&gt;easurable 📏
&lt;/h3&gt;

&lt;p&gt;With a measurable goal you can keep &lt;strong&gt;track of your progress&lt;/strong&gt; which will help you stay focused and motivated.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A&lt;/strong&gt;chievable 👌
&lt;/h3&gt;

&lt;p&gt;The goal should be realistic and attainable. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Les_Brown_(speaker)" rel="noopener noreferrer"&gt;Les Brown&lt;/a&gt;&lt;/strong&gt; once said: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Shoot for the moon. Even if you miss, you'll land among the stars"&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a great quote and I personally follow it when it comes to goals for things that I am passionate about. &lt;em&gt;If you put yourself outside of your comfort zone, you will be able to achieve greater things.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that said, some of your goals may be about things that you are not so passionate about. In that case, I recommend you &lt;strong&gt;play it safe&lt;/strong&gt; and aim for something that is relatively easy to achieve. Then, if you make it, you can raise the bar and continue on your growth journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;R&lt;/strong&gt;elevant 🧐
&lt;/h3&gt;

&lt;p&gt;The goal should be important to you and there should be a &lt;strong&gt;reason behind the goal&lt;/strong&gt;.  This will also help you stay motivated and will increase your chances of success.&lt;/p&gt;

&lt;p&gt;     Following the previous example, you may want to lose weight to be healthier and live longer. &lt;/p&gt;

&lt;p&gt;If you don't feel connected to your goal, you will rely only on &lt;em&gt;will power&lt;/em&gt; to achieve it. That could be ok for short term goals or if you have a strong will power. &lt;/p&gt;

&lt;p&gt;For long term goals, chances are that things will get in the way: a bad night of sleep, stress at work, etc. When that happens, being connected to your goal will help you persist on the efforts and eventually achieve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;T&lt;/strong&gt;ime bound ⏰
&lt;/h3&gt;

&lt;p&gt;It is important that you set a &lt;strong&gt;deadline&lt;/strong&gt; for your goal. This will not only help you stay motivated but ensure you take the time to evaluate whether you have succeeded or not.&lt;/p&gt;

&lt;p&gt;You should choose a deadline wisely. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give yourself enough time to achieve it. &lt;/li&gt;
&lt;li&gt;Feel free to stretch yourself if you are willing to take on the challenge.&lt;/li&gt;
&lt;li&gt;The goal must still be relevant once you achieve it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;     In the previous example, we picked one year for the weight loss goal. We could have picked 20 years for that goal if we were not sure we could make it. &lt;/p&gt;

&lt;p&gt;     Yet, if you are already experiencing health issues or if your doctor has given you a recommendation, reaching the goal in 20 years time might be too late.&lt;/p&gt;

&lt;h1&gt;
  
  
  3 - Goal setting in the middle of a pandemic
&lt;/h1&gt;

&lt;p&gt;You might be thinking ...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How can I come up with goals when everything around me is a mess?&lt;/p&gt;
&lt;/blockquote&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%2Fe9ob2btyylh7wla4h7h2.gif" 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%2Fe9ob2btyylh7wla4h7h2.gif" alt="Frustrated" width="244" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2020 was a crazy year and 2021 won't be any different for the time being. I admit that this volatile environment we live in is not ideal for self development. &lt;/p&gt;

&lt;p&gt;With that said, if you still have enough mental energy to do so, here are a &lt;strong&gt;couple of recommendations&lt;/strong&gt; that will help you succeed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Circle of influence
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="https://www.habitsforwellbeing.com/the-circle-of-concern-and-influence/" rel="noopener noreferrer"&gt;Circle of Influence&lt;/a&gt;&lt;/strong&gt; contains the things that concern you that you can do something about.&lt;/p&gt;

&lt;p&gt;&lt;strong&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%2F27stwx6cyqu97kn4ymt8.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%2F27stwx6cyqu97kn4ymt8.png" alt="Circle of influence" width="667" height="500"&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the context of goal setting, the idea is to choose goals that you are certain &lt;strong&gt;you can have an impact&lt;/strong&gt; on in this current climate.&lt;/p&gt;

&lt;p&gt;     As an example, I had a few goals in 2020 that involved traveling as a way to invest in my relationships with friends and family. Unfortunately, because of the travel restrictions, I failed in all of them.&lt;/p&gt;

&lt;p&gt;     For 2021, knowing that my circle of control has reduced because of COVID-related restrictions, I will look for alternative ways to achieve the same goal: video calls, texting, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkpoints
&lt;/h2&gt;

&lt;p&gt;This is an idea borrowed from the concept of &lt;strong&gt;&lt;a href="https://www.scrum.org/resources/what-is-a-sprint-retrospective#:~:text=As%20described%20in%20the%20Scrum,to%20the%20next%20Sprint%20Planning." rel="noopener noreferrer"&gt;sprint retrospectives&lt;/a&gt;&lt;/strong&gt; in agile development.&lt;/p&gt;

&lt;p&gt;By having &lt;strong&gt;regular checkpoints&lt;/strong&gt; on your way to the goal, you will have the opportunity to reassess whether the actions you chose during the GROW exercise are still applicable in the new reality. &lt;/p&gt;

&lt;p&gt;If you find that you no longer can continue with those actions or they are not as effective as you initially thought, you will still have time to pivot and choose a new &lt;strong&gt;way forward&lt;/strong&gt;. With that said, changing too often might be counterproductive and delay you over the long run.&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%2Fmemegenerator.net%2Fimg%2Finstances%2F62587151.jpg" 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%2Fmemegenerator.net%2Fimg%2Finstances%2F62587151.jpg" alt="Choose wisely" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important that you &lt;strong&gt;evaluate the tradeoffs&lt;/strong&gt; before you pivot on a new direction to reach the goal. You might be making progress even though you can't observe it just yet. &lt;/p&gt;

&lt;p&gt;     Going back to the example, many people who start exercising for weight loss purposes take longer to see results because their bodies go through a rebalancing process. They lose fat at the same time that they gain muscle and their net mass remains the same.&lt;/p&gt;

&lt;p&gt;     At this stage, changing the strategy completely may turn in a complete waste of effort. It may be more productive to pivot the way to measure progress to a metric that discerns between the two, such as body fat percentage.&lt;/p&gt;

</description>
      <category>goals</category>
      <category>career</category>
      <category>productivity</category>
      <category>advice</category>
    </item>
    <item>
      <title>2020 Goals in review</title>
      <dc:creator>Alberto Guerra González</dc:creator>
      <pubDate>Sat, 02 Jan 2021 15:54:57 +0000</pubDate>
      <link>https://dev.to/albertowar/2020-goals-in-review-5abj</link>
      <guid>https://dev.to/albertowar/2020-goals-in-review-5abj</guid>
      <description>&lt;p&gt;For many of us, the end of the year brings an ideal atmosphere for introspection. I don’t know if it is superstition, the need to escape from unbearably long family dinners and dodge poking questions about our relationship status; or simply the wish to stop repeating the same mistakes over and over again. At the end of 2019, I also couldn’t stop the urge to reflect and plan for the year to come.&lt;/p&gt;

&lt;p&gt;2020 was supposed to be not only the beginning of a new year but also the beginning of a new decade and the year I will turn 30. With the extra motivation, I rolled myself in a blanket, found a comfortable spot on the sofa and spent a good couple of hours crafting the perfect list of goals for 2020.&lt;/p&gt;

&lt;p&gt;Little had I known what was about to happen.&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%2Fimd3xnv75p7vkxyqt78r.gif" 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%2Fimd3xnv75p7vkxyqt78r.gif" alt="Chaos" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dealing with the unexpected
&lt;/h2&gt;

&lt;p&gt;2020 has been a hard year for many of us and a terrible year for many others. I believe it is important we acknowledge this and take the time to heal if we need to.&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%2F7lgox3tg0o3p2184owp0.gif" 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%2F7lgox3tg0o3p2184owp0.gif" alt="Heal" width="386" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personally, I try to focus on the learnings and the positives from this complicated year as a way to cope with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The outcome
&lt;/h2&gt;

&lt;p&gt;My list of 2020 goals could be divided in 4 areas of focus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Personal growth (3 proposed, 1.2 met)
&lt;/h3&gt;

&lt;p&gt;The goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blog writing&lt;/strong&gt;. I always wanted to share knowledge through blogs but never dared to do so. I wanted 2020 to be the year where I would overcome my impostor syndrome, step up and just do it. I set a goal of &lt;em&gt;at least 6 blog posts by the end of the year&lt;/em&gt; ❔&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn Go&lt;/strong&gt;. At the beginning of 2020, my team moved to a project where I needed to use Go for backend development. In order to prepare for that, I aimed to &lt;em&gt;complete Advent of Code in Go by the end of Q1 2020&lt;/em&gt; ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn Game Development&lt;/strong&gt;. I have been working for a video game developer company for a few years now, but I have learnt little about what game development is about. I believe it is important I understand the differences between what I do regularly and what game development is so I can collaborate with game teams more effectively. To do so, I aimed to &lt;em&gt;complete the Multiplayer Game Development Mini-Degree in Zenva by the end of the year&lt;/em&gt; ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although I didn’t meet the goal of 6 posts, since the goal was to start blog writing, I am happy I was able to take time to write publish my &lt;a href="https://dev.to/albertowar/lessons-learnt-from-my-first-mob-programming-session-5hjf"&gt;first blog post on mob programming&lt;/a&gt; and the post that you are reading now.&lt;/p&gt;

&lt;p&gt;When it comes to Go, I managed to learn enough to do what I needed to do at work, hence why I consider this goal met. However, I didn’t get enough time to finish Advent of Code in Go. I uploaded my progress to &lt;a href="https://github.com/albertowar/adventofcode" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; in case you are curious to see how far I got.&lt;/p&gt;

&lt;p&gt;Unfortunately, this year was quite busy at work with many tight deadlines. So I couldn’t find the time and energy to commit to explore game development.&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%2Fir9d3okzkiunkeico519.gif" 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%2Fir9d3okzkiunkeico519.gif" alt="Tired" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Health and wellness (5 proposed, 2.5 met)
&lt;/h3&gt;

&lt;p&gt;The previous year, I focused too much on doing great at work and I struggled to disconnect from it. Over time, this affected my sleep, which initiated a butterfly effect. With bad sleep, my energy levels plummeted, which influenced my mood and impacted the people around me. Something had to change, otherwise I was going to end up burning out.&lt;/p&gt;

&lt;p&gt;The goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sleep more&lt;/strong&gt;. At a minimum, go to bed before 11 PM &lt;em&gt;at least 3 times per week&lt;/em&gt; ❔&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exercise more&lt;/strong&gt;. Go to the gym &lt;em&gt;at least 3 times per week&lt;/em&gt; ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain reading habit&lt;/strong&gt;. Reading is one activity that helps me disconnect and relax. In 2019, I aimed to read one book per month and ended up reading about 30. This was something I enjoyed a lot and wanted to continue doing in 2020. For 2020, my goal was also &lt;em&gt;1 book per month&lt;/em&gt; ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch series &amp;amp; movies&lt;/strong&gt;. Similarly, this is another activity that helps me disconnect and I wanted to make time for it. At a minimum, I aimed for &lt;em&gt;one movie night per week&lt;/em&gt; ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Play more games&lt;/strong&gt;. Towards the second half of 2019, I stopped playing the games we develop in house and explored titles developed by other companies. Playing games is another activity that helps me disconnect, and I had an entire console generation to catch up with. For 2020, my goal was to play &lt;em&gt;at least 3 games developed by other studios&lt;/em&gt; ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although the &lt;em&gt;quality of the sleep improved&lt;/em&gt;, the number of hours was sometimes not enough. This year, it had more to do with the fact that we have been working with teams across multiple time zones (China and US West coast) which meant we had to start work early and finish late.&lt;/p&gt;

&lt;p&gt;With the series of lockdowns, tight deadlines we had to face at work and everything that was going on around the world, I didn’t have enough mental energy to exercise or read.&lt;/p&gt;

&lt;p&gt;To keep myself sane, I over-indexed in the other two activities which kept me going throughout the year.&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%2F53mksw5fj8h7qwwompdw.gif" 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%2F53mksw5fj8h7qwwompdw.gif" alt="Playing games" width="250" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are curious, I ended up spending a lot of hours in: &lt;em&gt;The Witcher 3, Dragon Ball Kakarot, Final Fantasy 7 Remake, AC Origins, AC Odyssey and AC Valhalla&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relationships (6 proposed, 1 met)
&lt;/h3&gt;

&lt;p&gt;If 2020 has taught us anything it was &lt;em&gt;the importance to spend time with our loved ones&lt;/em&gt;. Fortunately, this was something that I already wanted to change before the year started.&lt;/p&gt;

&lt;p&gt;The goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Travel to India in October&lt;/strong&gt;. To meet my partner’s family and spend at least one week there ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Travel to my hometown&lt;/strong&gt; in August and spend at least two weeks there ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move in with my partner by July&lt;/strong&gt; ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spend the end of 2020 also in my hometown&lt;/strong&gt;. To celebrate my 30th birthday with my friends there ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Travel to Lyon&lt;/strong&gt; to visit friends ❌&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Travel to Munich&lt;/strong&gt; to visit another friend ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can imagine, I couldn’t meet any of the goals that required traveling. Although flights were still operating, I cancelled all my travel plans as they were non essential.&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%2F9sdfnb5amp9vf4ir32mn.gif" 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%2F9sdfnb5amp9vf4ir32mn.gif" alt="Cancelled" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that said, I managed to move in with my partner before the lockdowns started. My previous apartment was close to a busy road, which didn’t help with my sleeping problems. So I pushed that goal a bit forward and we ended up moving together in &lt;em&gt;by the end of March&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finances and spending (3 proposed, 3 met)
&lt;/h3&gt;

&lt;p&gt;My master plan was to move in with my partner in 2020 and, if that went well, start searching for a house to buy together. In order to save for that deposit, there were certain things of my careless spending habits that I had to bring back in line.&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%2Fkd2qd55lkz84hcwzdhjy.gif" 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%2Fkd2qd55lkz84hcwzdhjy.gif" alt="Saving" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduce book purchases&lt;/strong&gt;. In 2019, I bought 22 books for a total of €257. Unfortunately, I only managed to read 13 of those books since I had plenty more in my library that were waiting for me to pick up. For 2020, I wanted to save some money by &lt;em&gt;cutting book purchases down to less than 10&lt;/em&gt;. ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce takeaway orders&lt;/strong&gt;. After living alone for many years, I had become a bit lazy when it comes to cooking and relied on Deliveroo more than I should have. Over the year, I made 14 orders and spent about €400 on it. This was another opportunity for me to save some money in 2020 and &lt;em&gt;reduce the number of orders to one per month at most&lt;/em&gt;. ✅&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximize company allowance&lt;/strong&gt;. My company has many perks I wasn’t fully leveraging in previous years (travel, game, sports, etc). This year I wanted to make sure I maximized them as much as I could. ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to books, I only bought &lt;em&gt;7 books in 2020&lt;/em&gt; but they were also a bit more expensive, so ended up saving only about €100 there.&lt;/p&gt;

&lt;p&gt;Deliveroo was a bit of a different story. The number of orders was also &lt;em&gt;14 this year&lt;/em&gt;, but some of them came from company allowance. The total was €100 over the previous year, but it was shared between two people, since I am now living with my partner. Overall, I think we did better than the previous year considering that we had to cook a lot more in 2020 because of the move to WFH.&lt;/p&gt;

&lt;p&gt;Overall, I &lt;em&gt;saved for the new house’s deposit&lt;/em&gt; and also realized that the impact of these spending habits to my overall saving power is not that great as I initially thought.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other achievements
&lt;/h3&gt;

&lt;p&gt;Apart from the goals I laid out at the beginning of the year, I have accomplished other things that I wasn’t initially planning for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I got &lt;strong&gt;promoted to Senior Software Engineer&lt;/strong&gt; 🥳&lt;/li&gt;
&lt;li&gt;We made an offer to buy a house that was accepted and we are in the &lt;strong&gt;process of purchasing it&lt;/strong&gt; 🥳&lt;/li&gt;
&lt;li&gt;Contributed to the &lt;strong&gt;launch of 3 games&lt;/strong&gt; 3️⃣✖️🎮&lt;/li&gt;
&lt;li&gt;Led an initiative to &lt;strong&gt;upgrade my team’s on-call process&lt;/strong&gt;, including product triage, reduce mis-escalations and onboarding process 🚨&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;2020 has been a hectic year and not much is going to change until the vaccine has been rolled out across the globe.&lt;/p&gt;

&lt;p&gt;With only 7.7 out of 17 (or 12 if we exclude the travel-related goals) met, you might think that I have missed a lot of the goals I signed up for, and you are right.&lt;/p&gt;

&lt;p&gt;However, &lt;em&gt;I strongly believe is important to celebrate our small victories&lt;/em&gt;, especially when they take place under less than ideal circumstances.&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%2Fcq2in9fls7cs3ozt4ia9.gif" 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%2Fcq2in9fls7cs3ozt4ia9.gif" alt="Celebrate" width="360" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As someone once said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Life is a marathon, not a sprint …&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every improvement we can make, regardless of how small, it is still a &lt;em&gt;step forward in the right direction&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed my 2020 goals story. I’m also planning to share what my 2021 goals will be in a follow up post where I will explain in detail the process I follow to come up with them.&lt;/p&gt;

&lt;p&gt;Keep tuned and stay safe!&lt;/p&gt;

</description>
      <category>goals</category>
      <category>softwareengineering</category>
      <category>personaldevelopment</category>
      <category>growth</category>
    </item>
    <item>
      <title>Lessons learnt from my first mob programming session</title>
      <dc:creator>Alberto Guerra González</dc:creator>
      <pubDate>Tue, 29 Sep 2020 09:28:52 +0000</pubDate>
      <link>https://dev.to/albertowar/lessons-learnt-from-my-first-mob-programming-session-5hjf</link>
      <guid>https://dev.to/albertowar/lessons-learnt-from-my-first-mob-programming-session-5hjf</guid>
      <description>&lt;p&gt;A few months ago, the YouTube recommender algorithm baited me with this presentation about Mob programming from Woody Zuill. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=28S4CVkYhWA" rel="noopener noreferrer"&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%2Ft7vwxn39hq8bym9ynsz0.jpg" alt="Mob Programming" width="480" height="360"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It was the first time I heard about the term, hence I followed my curiosity and gave it a go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On this post, I am going to walk you through the steps I took to introduce mob programming to my team and what I learnt from it.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  First impressions
&lt;/h1&gt;

&lt;p&gt;Wikipedia says that Mob programming is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Mob programming (informally mobbing) is a software development approach where the whole team works on the same thing, at the same time, in the same space, and at the same computer. [...]&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m sure you are thinking ...&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%2Fgeinepwbb1f8ny2at491.gif" 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%2Fgeinepwbb1f8ny2at491.gif" alt="Thinking" width="480" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How the **** can this be productive?&lt;/li&gt;
&lt;li&gt;If we don't even do pair programming at work, how can I convince my peers to try this?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s at least what I thought after watching the presentation 😅, so I moved on and a few months passed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Opportunity awaits
&lt;/h1&gt;

&lt;p&gt;Fast forward to a week ago, my team was feeling pretty drained. We had been battling deadline after deadline for the past year, and it has been long since the last time we did something fun together.&lt;/p&gt;

&lt;p&gt;This was the perfect moment to bring it up!&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%2F4buwa2i4sxe69fo5hliy.gif" 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%2F4buwa2i4sxe69fo5hliy.gif" alt="Idea" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I knew everyone would jump on anything that would pull them away from work, but it couldn't be so long that it could raise some manager’s eyebrows. &lt;/p&gt;

&lt;p&gt;With that in mind, I booked a one hour meeting in everyone's calendar on a Friday and hoped for the best.&lt;/p&gt;

&lt;p&gt;To my surprise, everyone loved the idea (even the managers 😮🎉). It was time to get to work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Preparation
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Mob programming agenda
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The key to a successful learning environment is structure - Cara Carroll&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first thing to figure out was the structure of the session. This would be a one hour trial, so I had to make sure I made the most out of the limited time.&lt;/p&gt;

&lt;p&gt;I broke the hour down as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;10 minutes for introduction&lt;/strong&gt;. Where I would explain the dynamics of pair programming and the problem we were about to solve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4x10 minutes blocks for problem solving&lt;/strong&gt;. This would ensure that a few of us would get a sense of what being a driver is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10 minutes for discussion&lt;/strong&gt;. This would allow me to get a sense of what my peers thought of the experiment and understand whether this is something we want to try for real.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Collaboration
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Together we are stronger - Lots of people&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next thing to define was how we were going to work &lt;strong&gt;together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Again, because of the time constraint, I decided to play it safe on this one so that the tools wouldn't slow us down: &lt;strong&gt;Git, Java, Maven.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;During the session, the designated driver would periodically commit to the repository (always making sure that the build was passing before that) and, once they were ready to pass the baton, they would push to the repository.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With that in mind, I created a repository with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Maven project with 1 class and 1 test class.&lt;/li&gt;
&lt;li&gt;A README with a brief explanation on mob programming, a description of the structure of the session and a list of tools needed to take part.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After that was ready, I added a link to the repository to the meeting invite and shared in Slack, so that everyone could setup before the session.&lt;/p&gt;

&lt;p&gt;In case you want to reuse something like this, I have pushed an example repository &lt;a href="https://github.com/albertowar/mob-programming" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Wax on, wax off - Mr. Miyagi&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a bit of browsing, I ran onto a site called &lt;a href="https://katalyst.codurance.com/" rel="noopener noreferrer"&gt;Katalyst&lt;/a&gt; from &lt;a href="https://codurance.com/" rel="noopener noreferrer"&gt;Codurance&lt;/a&gt; that has a ton of code &lt;a href="https://en.wikipedia.org/wiki/Kata_(programming)" rel="noopener noreferrer"&gt;katas&lt;/a&gt; to practice the craft. &lt;/p&gt;

&lt;p&gt;I picked a problem called &lt;a href="https://katalyst.codurance.com/string-calculator" rel="noopener noreferrer"&gt;String Calculator&lt;/a&gt;. My intention was to keep the focus of the session around &lt;strong&gt;getting familiar with mob programming&lt;/strong&gt;; therefore, I picked a simple problem that would not require a lot of brain cycles for experienced programmers.&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%2Fazjstfxp4b7z3x7z6ep4.gif" 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%2Fazjstfxp4b7z3x7z6ep4.gif" alt="Secret" width="480" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don't want people to start solving the problem in their heads and come with an idea on how to tackle it. We want to solve the problem &lt;strong&gt;together&lt;/strong&gt;. Do not share the problem prior to the session.&lt;/p&gt;

&lt;h1&gt;
  
  
  3-2-1, action!
&lt;/h1&gt;

&lt;p&gt;To ensure the session would go as expected, I played the role of a &lt;strong&gt;facilitator in disguise.&lt;/strong&gt;&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%2F73smapnblnagwkenztdh.gif" 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%2F73smapnblnagwkenztdh.gif" alt="Disguise" width="264" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was the first driver, so that everyone could see what that was about, and &lt;strong&gt;continuously probed individuals for ideas&lt;/strong&gt; on how to continue. This helped us to get the ball rolling and encouraged everyone to be engaged.&lt;/p&gt;

&lt;p&gt;Once my driver shift was over, I took a passive approach with small suggestions here and there but making sure that everyone would get their voices heard.&lt;/p&gt;

&lt;h1&gt;
  
  
  Key takeaways
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding&lt;/strong&gt;. I wanted to call this one out first because it caught me by surprise. We had a couple of engineers joining the team that week; and I noticed that it was the perfect way to show them how we code and what aspects we care about when improving code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bus factor → ∞&lt;/strong&gt;. With all the engineers working on the same code, the team is more resilient to attrition or people getting sick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It was productive&lt;/strong&gt;. I wasn't expecting this one but, with a lot more 👀 on the code, we were able to fly through the kata and almost finish it by the end of the session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge sharing&lt;/strong&gt;. One engineer was not familiar with the Java Stream syntax, and someone else explained how it works. Another engineer suggested using Regex to solve the problem, and everyone pitched in to discuss performance implications. This kept going spontaneously for the entire session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planted the seed&lt;/strong&gt;. Finally, the best takeaway was the fact that everyone started thinking about it. The next day, people already had ideas on how we could incorporate mob programming to specific areas of the next project to tackle 🥳. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mobprogramming</category>
      <category>pairprogramming</category>
      <category>productivity</category>
      <category>teamwork</category>
    </item>
  </channel>
</rss>
