<?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: Anton Ivanopoulos</title>
    <description>The latest articles on DEV Community by Anton Ivanopoulos (@antonivanopoulos).</description>
    <link>https://dev.to/antonivanopoulos</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%2F342121%2F8dd27f4c-3880-4f4a-93ba-dbdf5aae55b8.jpeg</url>
      <title>DEV Community: Anton Ivanopoulos</title>
      <link>https://dev.to/antonivanopoulos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antonivanopoulos"/>
    <language>en</language>
    <item>
      <title>Detecting new and shifting public holidays with Ruby</title>
      <dc:creator>Anton Ivanopoulos</dc:creator>
      <pubDate>Sat, 24 Sep 2022 06:38:04 +0000</pubDate>
      <link>https://dev.to/antonivanopoulos/new-and-shifting-public-holidays-with-ruby-g9h</link>
      <guid>https://dev.to/antonivanopoulos/new-and-shifting-public-holidays-with-ruby-g9h</guid>
      <description>&lt;p&gt;Our app has a few features that involve patient communications, specifically around not sending those communications out when there’s a public holiday (when a clinic might be closed and unable to handle incoming calls).&lt;/p&gt;

&lt;p&gt;Countries all have different holidays, but that also applies to regions and cities. Because people from all across the country use our app, we account for that. We’ll detect if there’s a holiday or not for a given set of users based on location, which will affect those comms going out.&lt;/p&gt;

&lt;p&gt;In the last couple of years, some interesting cases had confused customers because holidays started behaving weirdly, sending out things when we weren’t expecting to, or not sending things when we should.&lt;/p&gt;

&lt;p&gt;We use the holidays gem for detecting holidays on given dates, so I’ll go over what we ran into and how some previously-unused features of the gem helped us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We had a case of an existing public holiday shifting from when it would usually occur, and we needed to account for the new date.&lt;/li&gt;
&lt;li&gt;We had a case of a new, one-time public holiday that we needed to define ahead of time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;The holidays gem uses a series of yaml definition files that list all of the holidays in a given region. An example of one of these definitions is this one for New Year’s Day:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;months&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;New Year’s Day&lt;/span&gt;
      &lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;au&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_nsw&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_vic&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_act&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_sa&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_wa&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_nt&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;au_qld&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;mday&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;observed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;to_monday_if_weekend(date)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few parts here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You specify the month the holiday occurs.&lt;/li&gt;
&lt;li&gt;You can define more granular regions observe the holiday. In this case, New Year’s Day applies to all states in Australia as well as whole AU region.&lt;/li&gt;
&lt;li&gt;You can define the day of the month it occurs on (you can also specify the week number it occurs in and the day of that week)&lt;/li&gt;
&lt;li&gt;You can also define functions to handle the definition. In this case, handling moving the observed date to the following Monday if the holiday falls on a weekend. You can also just use a function to handle everything if things are particularly complex.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then when you want to check if there’s a holiday in a certain date, you can check it in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Holidays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;civil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2008&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;:au&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="no"&gt;ANZAC&lt;/span&gt; &lt;span class="no"&gt;Day&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Holidays that don’t want to sit still
&lt;/h2&gt;

&lt;p&gt;One of the interesting gotchas from the past couple of years with COVID is having to handle holidays that shift around due to extenuating circumstances.&lt;/p&gt;

&lt;p&gt;In this particular case, we had support tickets from confused Brisbane customers asking us why things weren’t being sent out as expected on a random Wednesday in August. It turns out this holiday had a series of interesting cases from 2020–2021. In 2020, it got moved to a Friday when it usually occurs on a Wednesday, and in 2021 it was moved to a later date (October some time). On this most recent occurrence in 2022, it was a case of the holiday being detected by &lt;code&gt;holidays&lt;/code&gt; on the wrong Wednesday in August.&lt;/p&gt;

&lt;p&gt;Taking a bit of a closer look, we found the holiday defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;8&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ekka&lt;/span&gt;
    &lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;au_qld_brisbane&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;week&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-3&lt;/span&gt;
    &lt;span class="na"&gt;wday&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It turns out that this mostly works, but, is actually a little &lt;em&gt;too&lt;/em&gt; simple for how the holiday actually occurs. As taken from the &lt;a href="https://www.qld.gov.au/recreation/travel/holidays/public"&gt;QLD public holidays page&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Royal National Agricultural (RNA) Show Day (Brisbane only) is held on the Wednesday during the RNA Show period. The RNA Show commences on the first Friday in August, unless the first Friday is prior to 5 August, then it commences on the second Friday of August.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the holiday has a bit of a dynamic aspect not currently being taken into account. On a &lt;a href="https://www.antonivanopoulos.com/hunting-down-spooky-json-module-redefinition-via-oj/"&gt;recent post&lt;/a&gt;, I had someone comment that they had used my post as a launchpad for making a change in an open source project, and that inspired me to go and sort this out myself. It ended up being &lt;a href="https://github.com/holidays/definitions/pull/229"&gt;a pretty simple change&lt;/a&gt; (my first proper OSS contribution, hell yeah), but essentially changes the holiday definition to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Ekka
  regions: [au_qld_brisbane]
  function: qld_brisbane_ekka_holiday(year)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With an accompanying function (I found writing this ruby function in yaml to be pretty funky but we got there):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;qld_brisbane_ekka_holiday&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# https://www.qld.gov.au/recreation/travel/holidays/public&lt;/span&gt;
  &lt;span class="c1"&gt;# The Ekka holiday occurs on the Wednesday during the RNA Show perido, the RNA show occurs on the first Friday of August, unless that’s prior to August 5, then it occurs on the second.  arguments: year&lt;/span&gt;
  &lt;span class="na"&gt;ruby&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;first_friday = Holidays::Factory::DateCalculator.day_of_month_calculator.call(year, 8, :first, :friday)&lt;/span&gt;
    &lt;span class="s"&gt;if first_friday &amp;lt; 5&lt;/span&gt;
      &lt;span class="s"&gt;second_friday = Date.civil(year, 8, Holidays::Factory::DateCalculator.day_of_month_calculator.call(year, 8, :second, :friday))&lt;/span&gt;
      &lt;span class="s"&gt;second_friday + 5 # The next Wednesday&lt;/span&gt;
    &lt;span class="s"&gt;else&lt;/span&gt;
      &lt;span class="s"&gt;Date.civil(year, 8, first_friday) + 5&lt;/span&gt;
    &lt;span class="s"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something I found pretty cool while putting this together is how the tests are done, they’re also in the yaml file. The development flow has you changing the branch that the &lt;code&gt;holidays/definitions&lt;/code&gt; submodule under &lt;code&gt;holidays/holidays&lt;/code&gt; is using, running some make commands to generate the functions and tests, then running those tests&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;given&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;‘2019–08–14’&lt;/span&gt;
    &lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;“au_qld_brisbane”&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;“Ekka”&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;given&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;‘2022–08–10&lt;/span&gt;
    &lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;“au_qld_brisbane”&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;“Ekka”&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added some extra tests for 2023/24, so we’re ready to rock when next year rolls around.&lt;/p&gt;

&lt;h2&gt;
  
  
  New public holidays
&lt;/h2&gt;

&lt;p&gt;With the recent passing of Her Majesty Queen Elizabeth II, it was announced that Australia would have a Nation Day of Mourning on September 22nd and that it would be a public holiday.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;holidays&lt;/code&gt; gem allows you to &lt;a href="https://github.com/holidays/holidays#loading-custom-definitions-on-the-fly"&gt;load in custom holidays&lt;/a&gt;, so we opted to go that route in the short term. We added a new initializer to load in a custom &lt;code&gt;holidays.yml&lt;/code&gt; file that contained the definition (shout out to Alex for putting this together):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/initializers/holidays.rb&lt;/span&gt;
&lt;span class="no"&gt;Holidays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="c1"&gt;#{__dir__}/holidays.yaml”)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then the definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Custom holiday definitions for the holidays gem (https://github.com/holidays/holidays)&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# See https://github.com/holidays/definitions/blob/master/doc/SYNTAX.md— -months:&lt;/span&gt;
  &lt;span class="na"&gt;9&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;National Day of Mourning for Queen Elizabeth II&lt;/span&gt;
      &lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;au&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;mday&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;
      &lt;span class="na"&gt;year_ranges&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limited&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;2022&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Moving forward
&lt;/h2&gt;

&lt;p&gt;This was all pretty straightforward stuff and I definitely don’t expect us to be needing to do these kinds of changes often (the definition for the Day of Mourning &lt;a href="https://github.com/holidays/definitions/pull/232"&gt;is already merged into the gem itself&lt;/a&gt;), but I really enjoyed getting a better look at the internals of this gem. Holidays are a bit of a hairy beast (especially when you have new ones popping up), but it was interesting to poke at some of the features on offer here that we hadn’t really had to play with before.&lt;/p&gt;

&lt;p&gt;Some final questions I’m left with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can you override the definition of an existing holiday with something new?&lt;/li&gt;
&lt;li&gt;Can you exclude holidays from the bundled definitions? (It seems like an open point of discussion in &lt;a href="https://github.com/holidays/holidays/issues/240"&gt;this issue&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Technical leadership during large refactors</title>
      <dc:creator>Anton Ivanopoulos</dc:creator>
      <pubDate>Wed, 11 May 2022 09:16:49 +0000</pubDate>
      <link>https://dev.to/antonivanopoulos/technical-leadership-during-large-refactors-29ml</link>
      <guid>https://dev.to/antonivanopoulos/technical-leadership-during-large-refactors-29ml</guid>
      <description>&lt;p&gt;A Long Time Ago™, we started a project at work to move our API serialisers from &lt;code&gt;ActiveModelSerializers&lt;/code&gt; to &lt;code&gt;Blueprinter&lt;/code&gt;. This kind of thing is hard. Hard to migrate a bunch of existing serialisers in prod on high traffic endpoints and be confident nothing will blow up in your face. Hard to get people motivated to migrate individual files and get things done. Hard to be sure that you've communicated the change correctly and that no one is left introducing more technical debt while you migrate.&lt;/p&gt;

&lt;p&gt;These are all issues that we had to deal with doing our migration. Our migration languished for a long time in a kind of limbo state where we had started using the new library. However, an extensive collection of legacy serialisers sat gathering dust, not migrated as they were in critical or commonly-used paths. There was a general trepidation around getting in there and changing things over.&lt;/p&gt;

&lt;p&gt;I chatted to team members who had contributed to the migration already, reflected on my own experiences, and identified some common concerns. In this article, I'll go through how we address the main hurdles we had with getting the project completed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It wasn't easy to maintain momentum because it wasn't clear what the end goal was.&lt;/li&gt;
&lt;li&gt;Even with the existing tests, people weren't confident that their changes would go out to production and everything would be fine.&lt;/li&gt;
&lt;li&gt;It was unclear where to start or how to get involved.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting goal posts and building momentum
&lt;/h2&gt;

&lt;p&gt;So there were two immediate issues I wanted to deal with before trying to start approaching the problem of actually migrating any files over.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we know when we've finished the work?&lt;/li&gt;
&lt;li&gt;How do we know people aren't adding more technical debt with the old library while we're migrating?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these were important problems to solve. It's hard to motivate folks to finish a job when they don't know how long is left to go, what the end state looks like, or in this case, how do we know when we're safe to remove the old library? And we want to remove as much friction as we can for other developers having to write serialisers during this period. No one wants to be confused about which bit of tech to use when starting something, and they don't want to have it pointed out to them in a code review after they've just finished doing their work.&lt;/p&gt;

&lt;p&gt;I was reminded of some work I'd done recently in another project where I used a custom Rubocop class to solve similar issues. They're handy because you can enforce a certain direction in the codebase on an ongoing basis, preventing anyone from doing the thing you're trying to avoid, while also providing more context in the error messages they'll receive. On top of that, the list of offences Rubocop would generate for the existing files will reflect the body of work we need to get through before we can consider the project completed.&lt;/p&gt;

&lt;p&gt;Here is what we ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rubocop'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;RuboCop&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cop&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Serializers&lt;/span&gt;
      &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PreferBlueprinter&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Base&lt;/span&gt;
        &lt;span class="no"&gt;BASE_PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'(const (const nil? :ActiveModel) :Serializer)'&lt;/span&gt;
        &lt;span class="no"&gt;MSG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Use Blueprinter, please!'&lt;/span&gt;

        &lt;span class="n"&gt;def_node_matcher&lt;/span&gt; &lt;span class="ss"&gt;:class_definition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;PATTERN&lt;/span&gt;&lt;span class="sh"&gt;
          (class (const _ _) &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;BASE_PATTERN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; ...)
&lt;/span&gt;&lt;span class="no"&gt;        PATTERN&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="n"&gt;class_definition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;add_offense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm still getting used to writing these. Still, &lt;a href="https://evilmartians.com/chronicles/custom-cops-for-rubocop-an-emergency-service-for-your-codebase"&gt;this article&lt;/a&gt; from Evil Martians has been a big help. The &lt;a href="https://github.com/rubocop/rubocop-rails/"&gt;rubocop-rails codebase&lt;/a&gt; also had some cops similar to what I wanted to put together. The cop we've put together checks if the class inherits from &lt;code&gt;ActiveModel::Serializer&lt;/code&gt; and adds an offence to that line.&lt;/p&gt;

&lt;p&gt;Now to get our todo list of classes we need to migrate. Running &lt;code&gt;bundle exec rubocop --format files --only Serializers/PreferBlueprinter&lt;/code&gt; will generate a list of files that cause offences for our new cop. We can add that to our rubocop.yml file as an exclude list for that cop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Serializers/PreferBlueprinter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app/serializers/old_serializer_a.rb'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app/serializers/old_serializer_b.rb'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app/serializers/old_serializer_c.rb'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That list is now how we're going to measure how close we are to being done. Each contribution to the migration will whittle that file down until we don't have anything left on the ignore list. After that, we can do a final PR to remove the cop and old library from our codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raising Confidence
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For each desired change, make the change easy (warning: this may be hard), then make the easy change.&lt;br&gt;
— &lt;cite&gt;Kent Beck&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, but we still haven't dealt with the root issue of people's low confidence and fear around changing these load-bearing serialisers. From chatting with the team, I learned two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People were less concerned about completely breaking things. There were enough tests to catch issues, and while annoying, we'll get alerts about that kind of thing and can roll back.&lt;/li&gt;
&lt;li&gt;They were &lt;em&gt;more&lt;/em&gt; concerned about subtler serialisation issues. How can we be sure that we're not introducing some small difference into the new serialiser that might cause issues?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what can we do? I thought we could add some form of safety net so that if things did go awry with our changes, we'd a) know so we could fix them and b) not adversely affect production. I landed on a version on &lt;a href="https://slack.engineering/re-architecting-slacks-workspace-preferences-how-to-move-to-an-eav-model-to-support-scalability/"&gt;dark reading&lt;/a&gt;, except here, we're comparing the results of two different serialisers rather than reading from two databases. If we detect any differences, we'd throw away the new one and fall back to the old JSON.&lt;/p&gt;

&lt;p&gt;The below helper class is a generalised version of what we ended up with (with extra comments around some of the decisions):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlueprinterMigrationFallback&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlueprinterValidationMismatch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:diff&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# For the original serialisation, we can just pass in the JSON output as is,&lt;/span&gt;
  &lt;span class="c1"&gt;# but, I chose to use a proc for the new serialisation so that we don't go through&lt;/span&gt;
  &lt;span class="c1"&gt;# the work of serialising it for requests that we don't need to.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ams_json&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;blueprinter_proc&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;ignore: &lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="n"&gt;sample_rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;production?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
    &lt;span class="n"&gt;sampled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rand&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;sample_rate&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ams_json&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;sampled&lt;/span&gt;

    &lt;span class="n"&gt;blueprinter_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blueprinter_proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

    &lt;span class="c1"&gt;# We found that some serialised fields _could_ be non-deterministic for&lt;/span&gt;
    &lt;span class="c1"&gt;# whatever reason, so we added support to ignore fields from a diff as&lt;/span&gt;
    &lt;span class="c1"&gt;# they would be different with each serialisation.&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hashdiff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ams_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blueprinter_json&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Raise our custom exception if there's a diff between the two versions.&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;BlueprinterValidationMismatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Resource failed to serialise with Blueprinter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;

    &lt;span class="c1"&gt;# Everything's all good, use our new serialisation!&lt;/span&gt;
    &lt;span class="n"&gt;blueprinter_json&lt;/span&gt;

  &lt;span class="c1"&gt;# Rescuing from StandardError here in case the proc we pass in throws some&lt;/span&gt;
  &lt;span class="c1"&gt;# error we're not expecting.&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;
    &lt;span class="c1"&gt;# Log the exception + the keys and operators involved in the diff. In our&lt;/span&gt;
    &lt;span class="c1"&gt;# app, this is where we capture the exception in Sentry so we can follow up later.&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test?&lt;/span&gt;

    &lt;span class="c1"&gt;# Return the original JSON.&lt;/span&gt;
    &lt;span class="n"&gt;ams_json&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a pretty simple class, but the safety net of not using the new serialiser unless it matches the old one should raise confidence in making the changes. The tradeoff is that you're doing the work twice for a subset of requests, and that's a potential gotcha depending on what you're serialising and how heavy it is.&lt;/p&gt;

&lt;p&gt;The other potential gotcha is the diffing itself, which can be a potentially long-running step depending on how big the payload is. We got bitten by that in one case where each record on the index action was also serialising thousands of associated records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting people on the same page
&lt;/h2&gt;

&lt;p&gt;Having added the helper class to the codebase and gone through a couple of migrations myself, I had a body of work I could point to as examples for others to follow.&lt;/p&gt;

&lt;p&gt;I compiled that info into a document that outlined the steps one would take to migrate one of the old classes, why we approached the problem this way and some additional use cases that other people might run into. The document also reiterated the problem statement, why we were changing serialisers in the first place and what we were hoping to get out of it. The project had been in limbo for long enough that it was worth communicating that info that might have been lost to the depths of Slack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;em&gt;are&lt;/em&gt; we even doing this?&lt;/strong&gt;&lt;br&gt;
If you've read &lt;a href="https://dev.to/antonivanopoulos/hunting-down-spooky-json-module-redefinition-via-oj-5609"&gt;my last post&lt;/a&gt; about the JSON module redefinition the Oj gem performs if you use it's &lt;code&gt;.optimize_rails&lt;/code&gt; method, I went into some detail around the potentially unexpected behaviour that that introduces, particularly in places you're not intending for it to happen (i.e. leaking out into another gem).&lt;/p&gt;

&lt;p&gt;This project started out for &lt;em&gt;other&lt;/em&gt; reasons, but, this framing helped to give it new life.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did we go?
&lt;/h2&gt;

&lt;p&gt;None of these changes meant that the work was instantly completed overnight, but they provided a framework for getting the job done. The project is moving along again, and we've made our way through a third of the files we need to migrate (50 of a set of 150).&lt;/p&gt;

&lt;p&gt;I'm really pleased with how this has turned out so far, and the process has given me more tools to approach managing a large cross-team change. I'll be sure to report back with anything new I learn as we make our way to the finish line!&lt;/p&gt;

&lt;p&gt;Just to wrap up, these were some of the key points we looked at as ways to better manage a large cross-team change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you can measure and report on progress. You need an end goal in sight and reporting on how you're moving can help keep the momentum up.&lt;/li&gt;
&lt;li&gt;Minimise risk. Make it easy for people to fail, they'll be more inclined to help out and get involved if they have fewer things to worry about.&lt;/li&gt;
&lt;li&gt;Document the change. Make sure you're able to articulate what you're doing and why, and what the benefits are.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know if you've had your own experiences or challenges going through change management in a way that gets people on board or makes it easy for others to get involved.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>refactorit</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Hunting down spooky JSON module redefinition via Oj</title>
      <dc:creator>Anton Ivanopoulos</dc:creator>
      <pubDate>Tue, 26 Apr 2022 09:16:11 +0000</pubDate>
      <link>https://dev.to/antonivanopoulos/hunting-down-spooky-json-module-redefinition-via-oj-5609</link>
      <guid>https://dev.to/antonivanopoulos/hunting-down-spooky-json-module-redefinition-via-oj-5609</guid>
      <description>&lt;p&gt;I've been spending some time recently to get on top of the dependabot alerts in our repo that have fallen by the wayside a little.&lt;/p&gt;

&lt;p&gt;Many of these are often a quick look over the changelog and approve, assuming the tests all pass; there might be some QA involved if anything is particularly spooky-looking or load-bearing. Then some upgrades need a bit of a closer look because the PR bumps several versions, and there are some breaking changes in the API, or the behaviour is now different, and things break in the app, not usually a massive drama.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new mystery
&lt;/h2&gt;

&lt;p&gt;Something more interesting than I ran into recently came up while upgrading the version of &lt;code&gt;fhir_client&lt;/code&gt; that we're using, from 4.0.3 to 5.0.3. Most of the changes looked relatively innocuous, except our test suite was now failing in a whole bunch of places with the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`has_key?' for nil:NilClass
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There was no specific place that was causing issues; it seemed to me whenever we were making a request via the client we had set up to hit an external API. Taking a quick trip to the issues for the project turned up &lt;a href="https://github.com/fhir-crucible/fhir_client/issues/147"&gt;an issue that looked suspiciously similar&lt;/a&gt; but with no actual outcome at the end of the thread barring some workarounds.&lt;/p&gt;

&lt;p&gt;So it seems like I was on my own here. Let's take a look at the full stack trace from the failures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`has_key?' for nil:NilClass

# /usr/local/bundle/gems/fhir_models-4.2.1/lib/fhir_models/bootstrap/json.rb:10:in `&lt;/span&gt;&lt;span class="n"&gt;pretty_generate&lt;/span&gt;&lt;span class="s1"&gt;'
# /usr/local/bundle/gems/fhir_models-4.2.1/lib/fhir_models/bootstrap/json.rb:10:in `to_json'&lt;/span&gt;
&lt;span class="c1"&gt;# /usr/local/bundle/gems/fhir_client-5.0.3/lib/fhir_client/client.rb:397:in `request_payload'&lt;/span&gt;
&lt;span class="c1"&gt;# /usr/local/bundle/gems/fhir_client-5.0.3/lib/fhir_client/client.rb:527:in `post'&lt;/span&gt;
&lt;span class="c1"&gt;# /usr/local/bundle/gems/fhir_client-5.0.3/lib/fhir_client/sections/crud.rb:207:in `base_create'&lt;/span&gt;
&lt;span class="c1"&gt;# /usr/local/bundle/gems/fhir_client-5.0.3/lib/fhir_client/sections/crud.rb:177:in `create'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cracking open the &lt;code&gt;fhir_models&lt;/code&gt; gem with &lt;code&gt;code $(bundle show fhir_models)&lt;/code&gt; on the update branch to get a look at the changes, I can see that the module having issues is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'JSON'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;FHIR&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;JSON&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pretty_generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could confirm with pry that trying to generate the JSON with &lt;code&gt;to_hash&lt;/code&gt; and &lt;code&gt;opts&lt;/code&gt; (which &lt;em&gt;was&lt;/em&gt; nil) was raising that error, and via &lt;code&gt;rails console&lt;/code&gt; that trying to pass other random hashes of data to &lt;code&gt;JSON.pretty_generate&lt;/code&gt; would also throw the same error when &lt;code&gt;opts&lt;/code&gt; was nil, but would be fine if &lt;code&gt;opts&lt;/code&gt; was an empty map.&lt;/p&gt;

&lt;p&gt;The mystery arose when looking at the source for &lt;code&gt;JSON.pretty_generate&lt;/code&gt;, where the definition of that method uses nil as the default value of &lt;code&gt;opts&lt;/code&gt;, and the handling should very much take care of a nil value.&lt;/p&gt;

&lt;p&gt;Things became a little clearer when trying to do the same test via &lt;code&gt;irb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;irb
&lt;span class="gp"&gt;irb(main):001:0&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;require &lt;span class="s1"&gt;'json'&lt;/span&gt;
&lt;span class="gp"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="gp"&gt;irb(main):002:0&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;JSON.pretty_generate&lt;span class="o"&gt;({&lt;/span&gt;a: &lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;, nil&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No error. I began to wonder if JSON was even the same thing here. My next lead was that there was something off in our app.&lt;/p&gt;

&lt;h2&gt;
  
  
  A case of mistaken identity
&lt;/h2&gt;

&lt;p&gt;I knew that our app was using &lt;a href="https://github.com/ohler55/oj"&gt;Oj&lt;/a&gt; for JSON serialisation. But I didn't initially notice anything spooky about &lt;code&gt;pretty_generate&lt;/code&gt;. A colleague of mine (shoutout Iain) had keener eyes and pulled up the C file that dealt with something spooky sounding in &lt;a href="https://github.com/ohler55/oj/blob/41f42231c886127b0467c771aceb16ec41d88159/ext/oj/mimic_json.c"&gt;mimic_json.c&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---yuS4mZT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d23wsn3xyuwvxakvv50r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---yuS4mZT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d23wsn3xyuwvxakvv50r.png" alt="The Let's See Who This Really Is meme, where the mask, labelled JSON has been removed to reveal the villain, labelled OJ" width="720" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reading up a bit more about the Rails setup for the Oj gem, the documentation suggests adding the following to an initialiser in your app to have the gem "take over" (quotes theirs) many methods of the JSON gem. That info, partnered with the C file, illuminated us to the fact that the &lt;code&gt;OJ.mimic_JSON&lt;/code&gt; method redefines the &lt;code&gt;JSON.pretty_generate&lt;/code&gt; method (among others) with a different implementation, one that either doesn't handle a nil value for opts or changes the handling to be focused on an empty hash.&lt;/p&gt;

&lt;p&gt;Taking a look back at our initialisers, I found one for Oj, one where we're calling &lt;code&gt;Oj.optimize_rails&lt;/code&gt;, of which &lt;code&gt;mimic_JSON&lt;/code&gt; is one of the steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving forward from a drop-in replacement
&lt;/h2&gt;

&lt;p&gt;We find ourselves here because we broadly do a lot of JSON generation as part of our API. We use &lt;code&gt;active_model_serializers&lt;/code&gt;, and the easiest way of getting the benefits of Oj's speedier JSON generation is to let it take over everything in one step making all of your serialisers more performant.&lt;/p&gt;

&lt;p&gt;On the other hand, the problem is that you're now rolling with all of those redefined methods any time you use the JSON module, even when you're not a) aware of it or b) intending to use them.&lt;/p&gt;

&lt;p&gt;In a happy coincidence, we're knee-deep in the middle of a migration of our serialisers to use &lt;a href="https://github.com/procore/blueprinter"&gt;Blueprinter&lt;/a&gt; instead of AMS. One benefit, in this case, is that it lets you explicitly set what generator you're using, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'oj'&lt;/span&gt;

&lt;span class="no"&gt;Blueprinter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Oj&lt;/span&gt; &lt;span class="c1"&gt;# default is JSON&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This whole scenario has fast-tracked our plans a little to finish off our migration so that we can remove gotchas like this and unblock our upgrade of the &lt;code&gt;fhir_client&lt;/code&gt; gem that we were looking for at initially.&lt;/p&gt;

&lt;p&gt;I always enjoy a bit of a spelunk like this. I'd be keen to hear of any other interesting scenarios that you might have run into that are similar.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>json</category>
    </item>
    <item>
      <title>TIL: Timecop has a safe mode</title>
      <dc:creator>Anton Ivanopoulos</dc:creator>
      <pubDate>Wed, 13 Apr 2022 23:20:18 +0000</pubDate>
      <link>https://dev.to/antonivanopoulos/til-timecop-has-a-safe-mode-5h8g</link>
      <guid>https://dev.to/antonivanopoulos/til-timecop-has-a-safe-mode-5h8g</guid>
      <description>&lt;p&gt;We use Timecop at work as a means of easily mocking the current date and being able to traverse around to simulate shifts in time. It's been an indispensable tool for testing out some particularly complex, time-sensitive components of our product.&lt;/p&gt;

&lt;p&gt;Something a little inconsistent throughout the codebase has been people's preference between using Timecop's block or not. The latter relies on the developer to put time back to normal by calling Timecop.return after they’re done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lost in Time
&lt;/h2&gt;

&lt;p&gt;Given that both &lt;code&gt;#freeze&lt;/code&gt; and &lt;code&gt;#travel&lt;/code&gt; mess with the app's understanding of what &lt;code&gt;Time.now&lt;/code&gt; is at any given moment, what happens if you don't return time to normal?&lt;/p&gt;

&lt;p&gt;This recently popped up as the cause of a few flakey tests that would rear their head in CI. We have a lot of tests in different areas that are sensitive to time. One, in particular, was popping up as a flake that was testing that a timestamp representing the last time a record was accessed was being updated. The flake that was popping up was failing intermittently because the timestamp was &lt;em&gt;not&lt;/em&gt; changing, even though after some initial poking around it seemed like the actual functionality was behaving just fine otherwise.&lt;/p&gt;

&lt;p&gt;It stood out to me that the failed assertion was seeing a very event time not being set, one that didn't involve any seconds or milliseconds. Having a quick search for that DateTime, I found another unrelated set of tests that were set up something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'context 1'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'context 2'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we had a context being set up in our test suite that was freezing time, but not putting it back to normal so that frozen time was leaking out and impacting other tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Safe Mode
&lt;/h2&gt;

&lt;p&gt;With things from &lt;a href="https://dev.to/til-queuing-sidekiq-workers-safely-with-the-help-of-isolator/"&gt;my last post&lt;/a&gt; in mind, I really didn't want this to become another thing where we were like, "you need to know about this gotcha forevermore whenever you're going to use Timecop". Luckily, a quick look at the docs showed that the library has &lt;a href="https://github.com/travisjeffery/timecop#timecopsafe_mode"&gt;a safe mode feature&lt;/a&gt; that enforces block syntax use. You can add this in your spec helper with the below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding this raised several errors in the test suite in places that were set up like the above contexts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SafeModeException&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="no"&gt;Safe&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="n"&gt;passing&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the tests failing, we now had a list of tests that we needed to go and amend and use the block syntax where necessary. A basic version of what that might look like is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'does something time-sensitive'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Some test setup and assertions&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end of your test, time will be back to the way it was without having to call &lt;code&gt;Timecop.return&lt;/code&gt;. This can get a little monotonous if you have a lot of tests in one area that is all time-sensitive, but you can still achieve something similar to &lt;code&gt;context 1&lt;/code&gt; above using the setup hooks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'context 1'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;around&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;Timecop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was pretty happy with how this turned out, a rather insidious cause for flakey tests had been ironed out, and the issue of consistency between the two ways of using the library was solved by being forced to use one of them.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>testing</category>
    </item>
    <item>
      <title>TIL: Queuing Sidekiq workers safely with the help of Isolator</title>
      <dc:creator>Anton Ivanopoulos</dc:creator>
      <pubDate>Tue, 16 Nov 2021 07:58:32 +0000</pubDate>
      <link>https://dev.to/antonivanopoulos/til-queuing-sidekiq-workers-safely-with-the-help-of-isolator-4c5b</link>
      <guid>https://dev.to/antonivanopoulos/til-queuing-sidekiq-workers-safely-with-the-help-of-isolator-4c5b</guid>
      <description>&lt;p&gt;Listening to a recent episode of the thoughtbot podcast, &lt;a href="https://podcasts.apple.com/au/podcast/the-bike-shed/id935763119"&gt;The Bike Shed&lt;/a&gt;, one of the hosts talked about a couple of gems that they'd come across that seemed pretty handy to me.&lt;/p&gt;

&lt;p&gt;The first is &lt;a href="https://github.com/palkan/isolator"&gt;&lt;code&gt;isolator&lt;/code&gt;&lt;/a&gt;, a gem by the folks over at Evil Martians that detects non-atomic interactions within a database transaction. What do we mean by that? A simple example taken from the documentation that uses background jobs is something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;confirmed_at: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;UserMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;successful_confirmation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_later&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some variation of the above is relatively common in many Rails codebases that I’ve worked with, more so when you start following the command pattern where you might have some service objects doing some work in a transaction.&lt;/p&gt;

&lt;p&gt;Another reasonably common sight is to defer work in a Rails model's hooks, usually &lt;code&gt;after_create&lt;/code&gt;, &lt;code&gt;after_update&lt;/code&gt;, &lt;code&gt;after_save&lt;/code&gt;, or even some combination of those. This particular set of hooks occur while the database transaction is still active. An example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:notify_author&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify_author&lt;/span&gt;
    &lt;span class="no"&gt;CommentMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;comment_created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_later&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isolator will throw errors at runtime whenever this kind of work happens inside a transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should we avoid this?
&lt;/h2&gt;

&lt;p&gt;Isolator appealed to me because I was in the middle of doing some of the plumbing behind migrating from DelayedJob to Sidekiq. The main difference here is that Sidekiq uses Redis as its backing data store. In contrast, DelayedJob uses a table in your database (I won't go into the pros and cons of either here).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using Redis as its data store, Sidekiq processes jobs very, very quickly. This is where we start running into problems when queuing workers mid-transaction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When enqueuing with Sidekiq (and Redis), there is a non-trivial chance that Sidekiq will pick up the job and start processing it before the transaction that queued the job has been committed to the database. In the case of the &lt;code&gt;Comment&lt;/code&gt; model, it might mean that when the delayed mailer runs to notify the author about a new comment, that comment doesn't exist yet when the job starts processing.&lt;/p&gt;

&lt;p&gt;How does isolator help here? The immediate benefit is knowing that this is happening in your dev or test environments. If we were to install the gem and create a new comment, we'd get an error something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Isolator::BackgroundJobError:
You are trying to enqueue background job inside db transaction.
In case of transaction failure, this may lead to data inconsistency and unexpected bugs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To solve this, we want to change our model to queue up the mailer in a transactional callback like &lt;code&gt;after_commit&lt;/code&gt; instead (we'll get to ad hoc transactions in a bit). We could rewrite the &lt;code&gt;Comment&lt;/code&gt; model above like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;after_create_commit&lt;/span&gt; &lt;span class="ss"&gt;:notify_author&lt;/span&gt;

  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my situation, isolator helped quite a lot because, with the relatively robust test coverage that we have, I immediately was able to identify several potential gotchas that would've happened if I'd just flipped over to Sidekiq as is.&lt;/p&gt;

&lt;p&gt;I can see a couple of other potential benefits by having this gem in our project. I find having a new, concrete error around that particular pattern makes for a more straightforward explanation about what's happening and why doing this work within a transaction is a potential foot-gun, as opposed to starting at "my background job behaves weirdly in production sometimes". Having the errors helps a lot when it comes to code reviews as well. In the same vein as gems like &lt;code&gt;strong_migrations&lt;/code&gt;, the author of the changes will get messages about these patterns ahead of time, and reviewers have one less thing to watch out for and can focus on other things.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about those ad hoc transactions?
&lt;/h2&gt;

&lt;p&gt;So changing the type of model hook solves our issue if we're queuing jobs up there, but what about that first example where we have a transaction we've started up manually in some other part of the code?&lt;/p&gt;

&lt;p&gt;Well, the Evil Martians folks have something else for that problem. The &lt;a href="https://github.com/Envek/after_commit_everywhere"&gt;&lt;code&gt;after_commit_everywhere&lt;/code&gt;&lt;/a&gt; gem allows you to use similar transactional callbacks outside of your models, so something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;after_commit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"We're all done!"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've not had a real opportunity to use this yet; I'm currently using the gem because it replaces the &lt;code&gt;after_commit&lt;/code&gt; hooks provided by &lt;code&gt;aasm&lt;/code&gt;, where we're queuing jobs on state changes. But, I like the syntax in theory; I think it reads pretty well, and if you're doing a bit of work inside a transaction, having that block at the bottom would express the intent clearly.&lt;/p&gt;

&lt;p&gt;I'm keen to see how this gem shakes out post-migration and how helpful we find it having the safety net there while folks get used to the patterns. Let me know if you've used either gem or if you have any other tools that help you with similar gotchas.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>todayilearned</category>
      <category>sidekiq</category>
    </item>
    <item>
      <title>Refocusing with smaller retros</title>
      <dc:creator>Anton Ivanopoulos</dc:creator>
      <pubDate>Tue, 09 Nov 2021 05:37:26 +0000</pubDate>
      <link>https://dev.to/antonivanopoulos/refocusing-with-smaller-retros-167e</link>
      <guid>https://dev.to/antonivanopoulos/refocusing-with-smaller-retros-167e</guid>
      <description>&lt;p&gt;I've had success with retros since I was introduced to them back in my first role as an engineer. I've always found them helpful from the perspective of improving on the more existential crises that teams I've been a part of run into and a good space for recognising the team's accomplishments as a whole.&lt;/p&gt;

&lt;p&gt;One problem I've tried to tackle more recently is when trying to reflect on issues specific to a smaller (or sub) team when doing retros with a broader team. When trying to raise issues, it can be hard to get the engagement of everyone present because they weren't running into those problems or lacked context. The lack of context was also a hard one to tackle because there was so much context to give in the first place and that usually came with a lot of past baggage that came kicking and screaming with it.&lt;/p&gt;

&lt;p&gt;Because I had a fair amount of autonomy in my position, I thought this marked a good time to introduce retros that focused on our product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the right people
&lt;/h2&gt;

&lt;p&gt;Right off the bat, I had a small group of people interested in meeting and reflecting on the work this small team was getting done. My direct report and I were the engineering team behind things, and we had a UX designer that had worked closely with us for two years at that point, and we had a breadth of experience to look back on that we'd not done at that point.&lt;/p&gt;

&lt;p&gt;We also had the idea of "product captains", members of the CX teams that would essentially be the experts on individual products we were offering. The product we owned had two, as there were two components to the product that, while similar, tackled different problems and had a lot of nuances that came with that.&lt;/p&gt;

&lt;p&gt;Having the product captains come along was one of my main goals for these smaller retros. They were knowledgeable about the product itself, but because of their customer-facing roles, they had a breadth of knowledge about things customers were running into that we might not be privy to yet.&lt;/p&gt;

&lt;p&gt;Not part of the formal proposition I was putting forward about starting these meetings up, but doing these smaller retros ticked a box for me as a manager. We'd recently gone remote at the start of the pandemic, and my direct report had only been with the team for a handful of months at that point. I found that introducing these retros to be a relatively small step we could take to get some more face time with other people in the business and some exposure to how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits and new reflections
&lt;/h2&gt;

&lt;p&gt;We now had a space for our small team to reflect on the work we were doing. Great!&lt;/p&gt;

&lt;p&gt;One thing that jumped out pretty early on, though (and I hoped this would be the case), is that this was not just a space for reflections about our engineering work. The product captains were bringing a lot of good material to us from their constant chats with customers. That brought about a lot of new conversations that wouldn't have come up in our regular engineering retro.&lt;/p&gt;

&lt;p&gt;Those conversations gave a lot of insight into how people were using the product, what they were missing, and if we missed the mark on things we released. It also gave us a new way to celebrate accomplishments because we now had a customer-centric source flavour to the feedback we were getting.&lt;/p&gt;

&lt;p&gt;On the flip-side, it gave the product captains a new sense of engagement and transparency that might have been lacking previously. They were now getting more actively involved in the product with a platform where they could raise their ideas and concerns, and they could get a feel for where we were at in terms of delivering the things we were working on, with a rough sense of how far away we might be from launching new features.&lt;/p&gt;

&lt;p&gt;Overall, the new meetings breathed some new life into the work this team was doing when we might have been feeling more disconnected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retro'ing the retros
&lt;/h2&gt;

&lt;p&gt;We were learning as we went with these new retros, and we found a few things early on that helped us be more effective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure everyone is on the same page around the purposes of the retro and what we're trying to get out of them. Not everyone had had experience doing retros before, so it helped get everyone familiar with the process. Interestingly this was one of my first times doing retros outside of an engineering org, and the level of engagement and the number of things raised was exciting.&lt;/li&gt;
&lt;li&gt;Talk about what you're trying to achieve by meeting regularly. I didn't want to throw everyone into a new recurring meeting because it solved some of my issues. But, I saw an opportunity to shape how we worked cross-functionally from now on, and communicating that was important. Framing the retros in that light led to some exciting conversations about team goals, how we measure our success and what aspirations we should have.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the whole, though, I think doing small-scale retros helped our team by giving us space to talk about our specific problems, and it gave us some new directions to explore now that we were getting different perspectives.&lt;/p&gt;

&lt;p&gt;I'd be keen to hear if anyone's tried something similar and what you got out of it, what worked and what didn't, and if you're still doing them.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>retrospectives</category>
      <category>remote</category>
    </item>
  </channel>
</rss>
