<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Industrial Logic - Better Software Sooner</title>
    <description>Industrial Logic is a pioneering Modern Agile consultancy that radically improves software development for organizations worldwide.</description>
    <link>https://www.industriallogic.com/</link>
    <atom:link href="https://www.industriallogic.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Wed, 25 Mar 2026 16:50:17 +0000</pubDate>
    <lastBuildDate>Wed, 25 Mar 2026 16:50:17 +0000</lastBuildDate>
    <generator>Jekyll v4.0.1</generator>
    
      <item>
        <title>Replace Hard-Coded Notifications with Observer</title>
        <description>&lt;p&gt;I was recently programming with Claude Code when we encountered a familiar smell: subclasses of domain objects that existed only to handle separate concerns, like database persistence.&lt;/p&gt;

&lt;p&gt;Together with Claude, we fixed the design flaw with &lt;a href=&quot;/refactoring-to-patterns/catalog/hardCodedNotificationsWithObserver&quot;&gt;Replace Hard-Coded Notifications with Observer&lt;/a&gt;—a refactoring from &lt;em&gt;Refactoring to Patterns&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;While &lt;em&gt;Refactoring to Patterns&lt;/em&gt; is now over 22 years old, it’s as relevant as ever, even in the era of agentic software development.&lt;/p&gt;

&lt;h2 id=&quot;the-smell&quot;&gt;The Smell&lt;/h2&gt;

&lt;p&gt;Our code has a domain class named &lt;code class=&quot;highlighter-rouge&quot;&gt;StationActivity&lt;/code&gt; that tracks player activity during a game.&lt;/p&gt;

&lt;p&gt;Two subclasses of &lt;code class=&quot;highlighter-rouge&quot;&gt;StationActivity&lt;/code&gt; had been created solely to persist game data to Firebase.&lt;/p&gt;

&lt;p&gt;Here’s one of those subclasses:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PlayerStationActivity&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StationActivity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;unresponsiveThresholdMs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PlayerRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;unresponsiveThresholdMs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastFlip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFlip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFlip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lastFlip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;saveFlip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roundNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;station&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dieIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startTime&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There was also a &lt;code class=&quot;highlighter-rouge&quot;&gt;HostStationActivity&lt;/code&gt; that did nearly the same thing for bot players.&lt;/p&gt;

&lt;p&gt;So we had two subclasses, both hard-coded to persist data to a repository, both passing seven parameters manually.&lt;/p&gt;

&lt;p&gt;The subclasses weren’t expressing behavioral variation—they were just injecting data and doing it from domain classes.&lt;/p&gt;

&lt;h2 id=&quot;the-refactoring&quot;&gt;The Refactoring&lt;/h2&gt;

&lt;p&gt;The fix had three parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Make the domain emit events instead of calling repositories directly:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// In game.updateDie()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastFlip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. Let the domain object graph provide context:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of passing seven parameters, add a &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt; getter to the batch that derives location from the object graph:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;BatchPath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;station&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eventId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;gameId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;station&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;roundNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;station&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roundNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;stationIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;station&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;batchId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. Subscribe at the application layer:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Player app: persist own flips&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;saveFlip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Host app: persist bot flips&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;player&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;station&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bot&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;saveFlip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;why-this-is-better&quot;&gt;Why This Is Better&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Aspect&lt;/th&gt;
      &lt;th&gt;Subclass Approach&lt;/th&gt;
      &lt;th&gt;Observer Approach&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Domain purity&lt;/td&gt;
      &lt;td&gt;Domain knows about repositories&lt;/td&gt;
      &lt;td&gt;Domain just emits events&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Parameter passing&lt;/td&gt;
      &lt;td&gt;7 params manually threaded&lt;/td&gt;
      &lt;td&gt;Data derived from object graph&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Visibility&lt;/td&gt;
      &lt;td&gt;Side effects hidden in subclasses&lt;/td&gt;
      &lt;td&gt;Side effects explicit at app layer&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Duplication&lt;/td&gt;
      &lt;td&gt;Two subclasses doing same thing&lt;/td&gt;
      &lt;td&gt;One unified &lt;code class=&quot;highlighter-rouge&quot;&gt;saveFlip()&lt;/code&gt; method&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Testability&lt;/td&gt;
      &lt;td&gt;Must mock repository in domain tests&lt;/td&gt;
      &lt;td&gt;Domain tests need no mocks&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The Observer pattern keeps the domain pure. The &lt;code class=&quot;highlighter-rouge&quot;&gt;Game&lt;/code&gt; class says “a flip happened” and moves on. It doesn’t know or care who’s listening or what they do with that information.&lt;/p&gt;

&lt;h2 id=&quot;the-pattern-in-action&quot;&gt;The Pattern in Action&lt;/h2&gt;

&lt;p&gt;This is the same pattern you use with DOM event listeners:&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// DOM&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;saveToDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Domain&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;saveFlip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;flip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;batch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The domain object is the event emitter. Application code subscribes with callbacks. When something happens, all subscribers get notified.&lt;/p&gt;

&lt;p&gt;This event-driven approach to persistence fits naturally in the JavaScript ecosystem, where EventEmitter patterns are idiomatic—from Node.js core to frontend frameworks. This decades-old refactoring isn’t just still applicable, it’s idiomatic.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Sidebar: How Does Claude Refactor Code?&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;When I mentioned refactoring “with Claude,” you might wonder how an AI actually edits code. Does it use an abstract syntax tree like an IDE?&lt;/p&gt;

  &lt;p&gt;No. Claude Code uses &lt;strong&gt;text-based editing&lt;/strong&gt;—reading files as plain text, searching with grep, and making edits via string replacement. There’s no AST, no symbol resolution, no type checker.&lt;/p&gt;

  &lt;p&gt;Instead, Claude relies on &lt;strong&gt;pattern recognition from training data&lt;/strong&gt;. When it sees a subclass that only calls &lt;code class=&quot;highlighter-rouge&quot;&gt;super&lt;/code&gt; and adds a side effect, it recognizes the smell—not by parsing node types, but because it’s seen millions of similar patterns.&lt;/p&gt;

  &lt;p&gt;This is both powerful (works with any language, can make judgment calls) and limited (might miss a reference, no compilation guarantees). It’s closer to how a human reads and edits code than how an IDE refactors it.&lt;/p&gt;

  &lt;p&gt;The decades-old patterns still guide the refactoring. The tools just read the code differently now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;when-to-apply-this-refactoring&quot;&gt;When to Apply This Refactoring&lt;/h2&gt;

&lt;p&gt;Look for these signs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Subclasses that only add side effects&lt;/strong&gt; (persistence, logging, analytics)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Multiple subclasses doing nearly identical things&lt;/strong&gt; to different targets&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Domain classes with repository dependencies&lt;/strong&gt; they shouldn’t have&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you spot these, consider &lt;a href=&quot;/refactoring-to-patterns/catalog/hardCodedNotificationsWithObserver&quot;&gt;Replace Hard-Coded Notifications with Observer&lt;/a&gt;. Your domain stays focused on business logic, and side effects move to the application layer where they belong.&lt;/p&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/refactoring-to-patterns/catalog/hardCodedNotificationsWithObserver&quot;&gt;Replace Hard-Coded Notifications with Observer&lt;/a&gt; in the Refactoring to Patterns catalog&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/refactoring-to-patterns/&quot;&gt;Refactoring to Patterns&lt;/a&gt; book&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 27 Jan 2026 18:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/replace-hard-coded-notifications-with-observer/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/replace-hard-coded-notifications-with-observer/</guid>
        
        <category>refactoring</category>
        
        <category>design patterns</category>
        
        <category>observer pattern</category>
        
        <category>event-driven</category>
        
        
        <category>Refactoring</category>
        
        <category>Software Design</category>
        
        <category>Patterns</category>
        
      </item>
    
      <item>
        <title>Safe Releases</title>
        <description>&lt;h2 id=&quot;series-index&quot;&gt;Series index&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/clean-start-protocol/&quot;&gt;Clean Start Protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/fast-feedback/&quot;&gt;Fast Feedback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/easy-integration/&quot;&gt;Easy Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-10-01-safe-release/SafeRelease-bubble.png&quot; alt=&quot;SafeRelease-bubble&quot; title=&quot;Number 5 in the series!&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;beginning-well&quot;&gt;Beginning Well&lt;/h1&gt;

&lt;p&gt;The old saying from Franklin Covey is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Begin with the end in mind&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Indeed, it is odd to &lt;em&gt;close&lt;/em&gt; the series with a blog about deployment and release when they are so fundamental to
success. Folks from the XP and code craft world see no reason to write a line of production code if they have not
already established version control, a test pipeline, and automatic deployment.&lt;/p&gt;

&lt;p&gt;Setting up a conventional application and pipeline is trivial and a bit tedious, so lately we find an LLM can do it
quite handily for us. We also find it useful to create a
few &lt;a href=&quot;https://agileotter.blogspot.com/2025/09/convenience-scripts-for-projectsw.html&quot;&gt;helper scripts&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;why-start-with-deployment&quot;&gt;Why start with deployment?&lt;/h1&gt;

&lt;p&gt;As we work iteratively and
in “&lt;a href=&quot;https://www.linkedin.com/pulse/working-small-skeletons-over-sprints-joshua-kerievsky/&quot;&gt;walking skeleton&lt;/a&gt;” style, we
let our sponsors and/or customers see our progress and assess our work in the “daily” builds.&lt;/p&gt;

&lt;p&gt;The running, tested code displays progress, inspires feedback, and does no damage to the company or customers.&lt;/p&gt;

&lt;p&gt;There is an advantage in that our work is accessible and visible, which reduces the urge of other people to micromanage
us.&lt;/p&gt;

&lt;p&gt;We can also do gap analysis between the new system we are developing and whatever processes we may be automating or
replacing. Whether it leads to a shorter development contract or a longer one, it should result in developing the
program that serves the customers’ needs best.&lt;/p&gt;

&lt;h1 id=&quot;why-not-wait-a-fortnight-to-deploy&quot;&gt;Why not wait a fortnight to deploy?&lt;/h1&gt;

&lt;p&gt;This is a common question.&lt;/p&gt;

&lt;p&gt;The answer is we CAN, of course. We can do nearly anything.&lt;/p&gt;

&lt;h2 id=&quot;its-more-work-if-we-wait&quot;&gt;It’s more work if we wait&lt;/h2&gt;

&lt;p&gt;If the change we’re testing and releasing is only five or six lines of code in two or three files, then it’s inexpensive
and effortless to release. If we have sufficient tests, then we are reasonably sure the changes are correct and
complete (enough).&lt;/p&gt;

&lt;p&gt;If we stockpile many hundreds (or, God forfend, thousands) of lines of change before we try to make a release, then it’s
a different story. There are too many things that can go wrong. There can be hidden bugs, big changes in security, big
changes in performance, or perhaps we have divergent changes where one set of changes makes another set invalid.&lt;/p&gt;

&lt;p&gt;The work of making a big release is a huge effort. Most people who release only once every year saw that they would need
as many as 6 months of hardening and testing to release. When they went to once a quarter, it took several weeks. When
they went to once every two weeks, it took only days.&lt;/p&gt;

&lt;p&gt;Small releases require small efforts.&lt;/p&gt;

&lt;h2 id=&quot;it-withholds-value&quot;&gt;It withholds value&lt;/h2&gt;

&lt;p&gt;Are you holding value hostage? If you are doing the most important work first, then the work that has been finished is
pent-up value.&lt;/p&gt;

&lt;p&gt;If you don’t release it, but rather wait for lower-value work to be done, then you are effectively holding high-value,
completed work hostage for low-value work completion.&lt;/p&gt;

&lt;p&gt;Does it make sense to withhold value from the project sponsors and users?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-10-01-safe-release/Hostage.png&quot; alt=&quot;/img/blog/2025-10-01-safe-release/Hostage.png&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is why we tend to ask what would happen if we released it today?&lt;/p&gt;

&lt;p&gt;We would like for that to be met with a business decision instead of not fear and panic.&lt;/p&gt;

&lt;p&gt;If it is always ready, it’s not scary.&lt;/p&gt;

&lt;h2 id=&quot;waiting-defers-feedback&quot;&gt;Waiting Defers Feedback&lt;/h2&gt;

&lt;p&gt;Let’s reiterate the process value of early feedback.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If all of our progress is visible at least daily, our sponsors and managers can see that we are working and plan for
our actual rate of progress.&lt;/li&gt;
  &lt;li&gt;Silence inspires micromanagement, whereas honest visibility inspires trust. Which do you find more pleasant and
useful?&lt;/li&gt;
  &lt;li&gt;If they can touch it and try it, they are likely to spot UI and UX issues our scripts cannot.&lt;/li&gt;
  &lt;li&gt;Every deployment attempt tests the delivery system and all the Infrastructure As Code (IaC) work we’ve done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So why would we want to wait?&lt;/p&gt;

&lt;h1 id=&quot;but-why-start-with-a-test-runner&quot;&gt;But why start with a test runner?&lt;/h1&gt;

&lt;p&gt;We fully intend to deliver many times a week, possibly many times a day.&lt;/p&gt;

&lt;p&gt;If we are delivering to a visible and accessible place (sandbox or production) where customers will interact with our
work, then we don’t want to ship any disasters to them.&lt;/p&gt;

&lt;p&gt;We use extensive automated testing to show that we’ve not broken any functionality (yet).&lt;/p&gt;

&lt;p&gt;We won’t have time for extensive manual testing.&lt;/p&gt;

&lt;p&gt;If this is true on the staging build, it’s doubly true of production.&lt;/p&gt;

&lt;h2 id=&quot;what-do-we-test&quot;&gt;What do we test?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;If code makes any decisions or transformations, we should have tests.&lt;/li&gt;
  &lt;li&gt;Changes to business behaviors and flows may require tests.&lt;/li&gt;
  &lt;li&gt;Every important E2E function needs automated checks.&lt;/li&gt;
  &lt;li&gt;Important exceptions need automated checks.&lt;/li&gt;
  &lt;li&gt;Content changes typically don’t require automated tests.&lt;/li&gt;
  &lt;li&gt;Cosmetic changes don’t generally need automated tests.&lt;/li&gt;
  &lt;li&gt;Pure plumbing code probably doesn’t need unit tests, as problems there will show up in a higher level of testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-do-we-test&quot;&gt;How do we test?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Microtests are unit tests that help us write and refactor code.&lt;/li&gt;
  &lt;li&gt;Unit Tests may be larger in scope than microtests, checking larger behaviors in code.&lt;/li&gt;
  &lt;li&gt;Component Test are those for entire subsystems or microservices.&lt;/li&gt;
  &lt;li&gt;End To End (e2e) tests ensure that the UI allows a user to perform a function.&lt;/li&gt;
  &lt;li&gt;Human Beings can tell if the system is easy enough to understand, to use, and to fit into the daily flow. They can
tell if it’s too lax about inputs or does not respect configuration options. They spot cosmetic issues, too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the things we cannot tolerate is &lt;a href=&quot;https://www.industriallogic.com/blog/why-are-my-tests-flakey/&quot;&gt;flaky tests&lt;/a&gt;. We
never want to deploy broken code. If tests are intermittent, then we may have code that doesn’t always work. Maybe the
test is poorly written, and the code is fine, but should we take that chance? Why aren’t the tests proof of function?&lt;/p&gt;

&lt;p&gt;Likewise, slow tests will delay our deployments. We want all our code to be deployed without error ASAP. To ensure this,
we like to run all our tests while we’re working on our development computers before pushing it to CI.&lt;/p&gt;

&lt;p&gt;I don’t see any of these as dispensable. They all seem deeply important if we are to make a new release in
minutes-to-hours from now.&lt;/p&gt;

&lt;h1 id=&quot;dont-ship-half-done-work&quot;&gt;Don’t Ship Half-Done Work!&lt;/h1&gt;

&lt;p&gt;We work in E2E slices, so the system is working early and often. Early slices are limited, but functionality grows more
competent and complete over time. All the parts that have been written work as an integrated (if thin) whole.&lt;/p&gt;

&lt;p&gt;Thin slicing like this is helpful if you wish to allow your sponsors to say that’s enough of this feature, let’s
move on to something more important.&lt;/p&gt;

&lt;p&gt;Top secret pro tips:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sometimes our sponsors and BAs will realize after we’ve done the most important 50% of a feature that the remainder
isn’t all that important&lt;/li&gt;
  &lt;li&gt;It can be helpful to our managers if they can cut scope in order to make their promise dates. If we deliver all the
work they specified in one fell swoop, we rob them of the opportunity.&lt;/li&gt;
  &lt;li&gt;Remember what I repeatedly said about micromanaging?&lt;/li&gt;
  &lt;li&gt;You are more &lt;a href=&quot;https://www.industriallogic.com/blog/work-to-be-interruptible/&quot;&gt;safely interruptable&lt;/a&gt; if you’re &lt;strong&gt;not&lt;/strong&gt;
trying to keep a huge changeset in your head.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may find that the visual verification of progress and quality is far more valuable than the convenient familiarity
of working on huge changes before eventually merging and deploying.&lt;/p&gt;

&lt;h1 id=&quot;is-this-asking-too-much&quot;&gt;Is This Asking Too Much?&lt;/h1&gt;

&lt;p&gt;Many companies deliver software to production many times per day. Even our small website group can deliver a dozen
times a day to production. This isn’t unheard-of or strange, it’s the practice known
as &lt;a href=&quot;https://www.industriallogic.com/blog/benefits-continuous-deployment/&quot;&gt;Continuous Deployment.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every team has constraints and context that may make some of these practices challenging. We understand that. It doesn’t
make
the work impossible.&lt;/p&gt;

&lt;p&gt;I am a huge fan of working in a Trunk-Based way and delivering to production many times a day, but I have worked in
regulated environments and mixed hardware/software where this kind of change isn’t as straightforward. In one project,
we worked on delivering to a hardware-in-loop test environment at least until they were able to make a virtual hardware
environment and deliver to it.&lt;/p&gt;

&lt;h1 id=&quot;do-we-have-to-work-this-way&quot;&gt;Do We Have To Work This Way?&lt;/h1&gt;

&lt;p&gt;No. I can’t tell you what you have to do. I’m not the boss of you.&lt;/p&gt;

&lt;p&gt;OTOH, you may be having trouble making releases safely. The old saying is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you keep planting the same seeds,
you will keep harvesting the same crop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may be in a team where the &lt;a href=&quot;https://dora.dev/guides/dora-metrics-four-keys/&quot;&gt;DORA metrics&lt;/a&gt; are being tracked. If so,
then the person who really is your boss is telling you to work in a similar way so you improve the measured
capabilities.&lt;/p&gt;

&lt;p&gt;If you follow all the articles in this series, it should help you avoid or minimize many of the problems organizations
face in moving to continuous delivery.&lt;/p&gt;

&lt;p&gt;If you aren’t using the DORA metrics, you are still doing the right thing if:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You can develop the deployment process with tight feedback loops&lt;/li&gt;
  &lt;li&gt;Deployment becomes a frequent non-event&lt;/li&gt;
  &lt;li&gt;You are able to give real, verifiable proof of progress as it happens&lt;/li&gt;
  &lt;li&gt;You experience high pass rates through the quality gates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wish you well.&lt;/p&gt;

</description>
        <pubDate>Wed, 01 Oct 2025 10:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/safe-release/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/safe-release/</guid>
        
        <category>Safe Release</category>
        
        <category>at scale</category>
        
        <category>delivery problems</category>
        
        <category>slow delivery</category>
        
        <category>missed schedules</category>
        
        <category>roll back</category>
        
        <category>failed release</category>
        
        
        <category>Continuous Delivery</category>
        
        <category>Continuous Integration</category>
        
        <category>Development</category>
        
        <category>Evolutionary Design</category>
        
        <category>Extreme Programming</category>
        
        <category>Process Improvement</category>
        
        <category>Risk Management</category>
        
        <category>Software Development</category>
        
        <category>Tech Safety</category>
        
      </item>
    
      <item>
        <title>Easy Integration</title>
        <description>&lt;h2 id=&quot;series-index&quot;&gt;Series index&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/clean-start-protocol/&quot;&gt;Clean Start Protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/fast-feedback/&quot;&gt;Fast Feedback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/easy-integration/&quot;&gt;Easy Integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/safe-release/&quot;&gt;Safe Releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-03-26-easy-integration/main-banner-large.png&quot; alt=&quot;introductory image&quot; title=&quot;Number 4 in the series!&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;what-makes-integration-hard&quot;&gt;What Makes Integration Hard?&lt;/h1&gt;

&lt;p&gt;Software systems often need changes.&lt;/p&gt;

&lt;p&gt;Sometimes, the goal is to reduce runtime costs. In other cases, the focus is on improving performance. Changes are
frequently made to add new features or capabilities. Changes are needed to correct defects or inadequacies within the
system.&lt;/p&gt;

&lt;p&gt;A developer (or team) starts with an approved, stable version of the system’s source code. They create a set of
changes (a “changeset”) to introduce new functionality or improve software behavior.&lt;/p&gt;

&lt;p&gt;Any codebase will have many interacting constraints and rules. Much of the art of software development involves making
new changes while remaining compatible with existing functionality.&lt;/p&gt;

&lt;p&gt;Team A is working on a change and started with version A of the main development line, which works fine as an isolated
change.&lt;/p&gt;

&lt;p&gt;Team B is working on a change also based on version A of the main code line, and their change also works fine.&lt;/p&gt;

&lt;p&gt;The problem shows up much later. While each change works independently, the two don’t work together.&lt;/p&gt;

&lt;p&gt;As long as changes are isolated, the developers are unaware of the defects they injected.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-03-26-easy-integration/risk-of-change.png&quot; alt=&quot;illustration showing my uncommitted changes vs your uncommitted changes as the risk of collision&quot; title=&quot;Ya don&apos;t know what ya don&apos;t know&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;
&lt;figcaption class=&quot;text-center&quot;&gt;You can&apos;t ensure compatibility if you can&apos;t see what&apos;s coming.&lt;/figcaption&gt;

&lt;p&gt;When an integration issue (an incompatible set of changes) surfaces, the developers must identify the underlying issues,
locate the affected regions of code, and make the necessary design changes.&lt;/p&gt;

&lt;p&gt;This rework usually falls to the team whose code changes were merged last. The first team’s changes integrate fine. This
often makes the earlier-merged team seem superior to the teams whose code was equally valid but whose changeset was
added just before the error was spotted.&lt;/p&gt;

&lt;p&gt;When blame is given, it’s the “luck of the draw” who receives the blame. “It was fine before we added your code” doesn’t
mean that the team merged last did a poor job. This is counterintuitive but worth considering. If the merges had
happened in the opposite order, the other team would seem to be at fault.&lt;/p&gt;

&lt;p&gt;The problem is that neither team could tell that their changes would not mesh
with the other team’s changes.&lt;/p&gt;

&lt;p&gt;Large changesets are not only more likely to hide incompatibilities, but they are also
harder to diagnose and repair.&lt;/p&gt;

&lt;p&gt;The larger the changesets are, the greater the risk of divergence and the more complicated the remediation may be.&lt;/p&gt;

&lt;p&gt;If incompatibilities go unnoticed until late in the process, you can expect:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;delayed releases&lt;/li&gt;
  &lt;li&gt;missed release schedules&lt;/li&gt;
  &lt;li&gt;flawed releases that must be rolled back&lt;/li&gt;
  &lt;li&gt;error reports from customers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are common problems in corporate software development arising from the same underlying cause.&lt;/p&gt;

&lt;p&gt;These issues don’t occur in single-developer projects or when a small, tight team works on a greenfield project. You
simply don’t encounter these problems when only one group of people works on one codebase with a single changeset at a
time.&lt;/p&gt;

&lt;p&gt;People from small, nimble startups who join their first large corporate development team are unprepared for problems of
scale. The same is true for university graduates who have mostly worked solo on class assignments or in small groups.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Integration problems are problems of scale.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Significant factors for these integration problems are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The number of parallel changes&lt;/li&gt;
  &lt;li&gt;The size of each changeset&lt;/li&gt;
  &lt;li&gt;Deferring discovery of issues until it’s too late to recover gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;how-to-reduce-difficulty&quot;&gt;How To Reduce Difficulty&lt;/h1&gt;

&lt;h2 id=&quot;limit-the-number-of-parallel-activities&quot;&gt;Limit The Number of Parallel Activities&lt;/h2&gt;

&lt;p&gt;Sharing a codebase across many teams requires finesses and solid product knowledge, but several time-honored approaches
are used with some success.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Only do one thing to a product at a time&lt;/strong&gt;. This is hard to do at scale, because organization grow
primarily to achieve more than one thing at a time. If you can do only
one thing at a time in one codebase, you eliminate all the chances for collision.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Parallel single new feature work with fixes&lt;/strong&gt;. Defects are (by nature)
identified in existing code, whereas new features tend to involve creating new code. There may be some overlap, but it
is minimized. Separating work this way may run counter to business priorities when the focus is on new features, and
there is still a risk that some oversized core modules will receive conflicting changes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ensure that parallel features affect different areas of the codebase&lt;/strong&gt;. Doing this requires intimate knowledge of
the source code since things that seem unrelated in the eyes of users (and non-technical managers) may connect tightly
in the codebase. This also may run counter to business priorities by delaying highly desired but risky, changes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Limit &lt;a href=&quot;https://agileotter.blogspot.com/2010/04/linear-and-radial-progress-means-taking.html&quot;&gt;radial changes&lt;/a&gt;&lt;/strong&gt;
while implementing a feature can minimize merge collisions but also leaves the code with accumulated neglect (often
called &lt;em&gt;technical debt&lt;/em&gt;). There must be a follow-on task to return to the code and rationalize the design. This
technique trades one set of technical risks for another, so be sure that you are solving the more troubling issue.&lt;/li&gt;
  &lt;li&gt;More &lt;strong&gt;modularity of design&lt;/strong&gt; and the &lt;strong&gt;use of interfaces&lt;/strong&gt; can help to make parts of the system independently
modifiable. This is one of the useful features of Hexagonal Architecture.&lt;/li&gt;
  &lt;li&gt;While I have no significant experience in &lt;strong&gt;Event Driven Architecture&lt;/strong&gt;, I am often told that it solves a lot of
problems via modularity and repeatability, so that one can replace entire services without upset, and can run old
versions of an app in parallel with new ones to judge readiness for switch-over.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoiding incompatible changesets is an attractive idea, but hard to achieve for management. It requires intimate
knowledge of the code and a commitment to rationalizing the design after adding features.&lt;/p&gt;

&lt;p&gt;I wouldn’t rely on trying to avoid the issue since doing so lacks safety.&lt;/p&gt;

&lt;p&gt;Once a collision or incompatibility sneaks into the changeset, if you spot it, you pay the full price to remedy the
situation.&lt;/p&gt;

&lt;p&gt;Let’s see what else we can do either in addition or instead of avoiding the problem.&lt;/p&gt;

&lt;h2 id=&quot;reduce-period-of-isolated-code-development&quot;&gt;Reduce Period of Isolated Code Development&lt;/h2&gt;

&lt;p&gt;If we do less work between integrations, we have smaller changesets with a lower chance of collision. Less code also
means less effort to isolate and correct a fault.&lt;/p&gt;

&lt;p&gt;We can have smaller changesets if
we &lt;a href=&quot;https://agileotter.blogspot.com/2022/03/splitting-stories-resource-list.html&quot;&gt;break our features into smaller sub-features&lt;/a&gt;,
each one a &lt;a href=&quot;https://www.youtube.com/watch?v=tnVeBny9UOM&amp;amp;ab_channel=%23ModernAgileShow&quot;&gt;fully working subset&lt;/a&gt; of the
planned, final result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only one small change per changeset&lt;/strong&gt; makes each changeset small, and ensures they are
completed more often.&lt;/p&gt;

&lt;p&gt;This will mean more integrations, but each will be simpler.&lt;/p&gt;

&lt;p&gt;We use many story-splitting techniques such as Role-Action-Context exercises, Use Case Mapping, Story Mapping, and
Example Mapping. The specific technique you choose is less important than the act of splitting a feature into a series
of fully-working “value drops” or “slices.”&lt;/p&gt;

&lt;p&gt;This
is &lt;a href=&quot;https://www.craiglarman.com/wiki/downloads/misc/history-of-iterative-larman-and-basili-ieee-computer.pdf&quot;&gt;iterative and incremental&lt;/a&gt;
development.&lt;/p&gt;

&lt;p&gt;The increment must be fully working, even if the feature implementation is not comprehensive. It is best if it has a
suite of tests to ensure that it is fully
working and that some other feature doesn’t “break” its functionality.&lt;/p&gt;

&lt;p&gt;The next increment may broaden the functionality of that feature. In this way, the feature is “grown” in a series of
releases, rather than “constructed” in a single pass. This means that we may be integrating just a handful of lines of
code at a time, all tightly focused on specific behaviors in the code.&lt;/p&gt;

&lt;p&gt;The small changesets and tight focus on a narrow bit functionality reduces the opportunity for collisions. It also
reduces the cost of collisions.&lt;/p&gt;

&lt;p&gt;We often see teams with smaller changesets completing incremental changes many times a day or at least several times per
week.&lt;/p&gt;

&lt;h2 id=&quot;find-problems-sooner&quot;&gt;Find Problems Sooner&lt;/h2&gt;

&lt;p&gt;We don’t want to find out about large, incompatible changesets on the last day before a release.&lt;/p&gt;

&lt;p&gt;The longer we hold changesets in isolation (for instance, in git branches or on someone else’s machine), the longer it
takes to discover incompatibilities.&lt;/p&gt;

&lt;p&gt;This is because when we merge two branches, we produce a new version of the product’s source code; one that has
never existed or been tested. There was no opportunity to discover the problem early and have a solution already in
hand.&lt;/p&gt;

&lt;p&gt;The term “continuous integration” (CI) is described and discussed at
the&lt;a href=&quot;https://wiki.c2.com/?ContinuousIntegration&quot;&gt; C2 wiki&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What if engineers didn’t hold on to modules for more than a moment? What if they made their (correct) change, and
&lt;em&gt;presto!&lt;/em&gt; everyone’s computer instantly had that version of the module?
You wouldn’t ever have &lt;a href=&quot;https://wiki.c2.com/?IntegrationHell&quot;&gt;Integration hell&lt;/a&gt; because the system would always be
integrated. […] there wouldn’t be any conflicts to worry about.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since this work is aided by a service that automatically rebuilds and re-tests (well, runs the automated checks and
scans at least) when changes are made, people often think of those build-and-test servers as “CI Servers.”&lt;/p&gt;

&lt;p&gt;CI is not the act of running a build-and-test server. CI is a developer behavior, not a technical appliance.&lt;/p&gt;

&lt;p&gt;A CI server itself will usually fail if the code cannot be compiled or if a scanner reports a seriously ill-formed bit
of code, but the server doesn’t know if the compiled code actually works and can carry out useful tasks.&lt;/p&gt;

&lt;p&gt;We need fast, automated tests that can run in the CI server and flag issues as they occur.&lt;/p&gt;

&lt;p&gt;This suite of tests needs to match the current functionality of the program, growing as the codebase grows.&lt;/p&gt;

&lt;p&gt;We need some kind of continuous testing and continuous development of automated tests.&lt;/p&gt;

&lt;p&gt;By the way, if you have achieved TBD and CI, there is little reason to not go all the way to Continuous Deployment (to a
machine) or even Continuous Delivery (to production).&lt;/p&gt;

&lt;h1 id=&quot;is-this-asking-too-much&quot;&gt;Is This Asking Too Much?&lt;/h1&gt;

&lt;p&gt;When one is accustomed to working with a small team in a greenfield project, this all seems overblown and inconvenient.&lt;/p&gt;

&lt;p&gt;Without doing something to mitigate these problems, your large projects will suffer from unpredictability, errors,
delays, defects, and rolled-back releases.&lt;/p&gt;

&lt;p&gt;Does this happen? All the time.&lt;/p&gt;

&lt;p&gt;Do they still release software? Yes, but releasing is full of problems.&lt;/p&gt;

&lt;p&gt;These problems are inherent in the system of work.&lt;/p&gt;

&lt;p&gt;If the system remains unchanged, then other attempts to mitigate the problem (like increasing the reporting burden or
replacing programmers or managers) will make little positive difference and may worsen the situation.&lt;/p&gt;

&lt;p&gt;If you change the system, you don’t have the same problems.&lt;/p&gt;

&lt;h1 id=&quot;do-we-have-to-work-this-way&quot;&gt;Do We Have To Work This Way?&lt;/h1&gt;

&lt;p&gt;You are doing the right thing if you can address these three problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Likelihood of incompatible changes&lt;/li&gt;
  &lt;li&gt;Size of incompatible changesets&lt;/li&gt;
  &lt;li&gt;Late identification of problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are not having any problems with incompatible changes, late failures, merge collisions, or surprising errors,
then whatever you are doing is working.&lt;/p&gt;

&lt;p&gt;If you keep having these problems and don’t want to fix them, then that’s your choice, but maybe looking at the picture
from this point of view may help you understand what your people are going through.&lt;/p&gt;

&lt;p&gt;I wish you well regardless.&lt;/p&gt;
</description>
        <pubDate>Wed, 26 Mar 2025 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/easy-integration/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/easy-integration/</guid>
        
        <category>easy integration</category>
        
        <category>incompatible change</category>
        
        <category>divergence</category>
        
        <category>merge conflicts</category>
        
        <category>integration problems</category>
        
        <category>at scale</category>
        
        <category>delivery problems</category>
        
        <category>slow delivery</category>
        
        <category>missed schedules</category>
        
        <category>roll back</category>
        
        <category>failed release</category>
        
        <category>quality errors</category>
        
        <category>parallel change</category>
        
        
        <category>Continuous Delivery</category>
        
        <category>Continuous Integration</category>
        
        <category>Development</category>
        
        <category>Evolutionary Design</category>
        
        <category>Extreme Programming</category>
        
        <category>Process Improvement</category>
        
        <category>Risk Management</category>
        
        <category>Software Development</category>
        
        <category>Tech Safety</category>
        
      </item>
    
      <item>
        <title>Shouldn&apos;t It Be Faster?</title>
        <description>&lt;p&gt;&lt;img src=&quot;/img/blog/2025-parallel-efforts/featured.png&quot; alt=&quot;featured image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;too-slow&quot;&gt;Too Slow&lt;/h1&gt;

&lt;p&gt;Developing software takes longer than we want.&lt;/p&gt;

&lt;p&gt;In hopes of getting work done sooner, teams (or their managers) divide jobs into tasks
that can be performed asynchronously&lt;sup id=&quot;fnref:divideandconquer&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:divideandconquer&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The assumption that dividing up is faster is seldom challenged. It’s really a 
matter of faith rather than measurement. It is a familiar habit and the ticket
systems support doing it this way, so it’s comfortable and familair.&lt;/p&gt;

&lt;p&gt;Teams have been consistently losing the parallel game for years but never realized 
it because they never measured or had any way to reason about time being saved
or lost; it is a matter of faith and familiary.&lt;/p&gt;

&lt;p&gt;Sometimes it works incredibly well, though.&lt;/p&gt;

&lt;p&gt;Is this random?&lt;/p&gt;

&lt;p&gt;Is it luck?&lt;/p&gt;

&lt;p&gt;Does it only work when the people are hard-core and good enough?&lt;/p&gt;

&lt;p&gt;Is there some rational, logical reason why it’s only sometimes successful?&lt;/p&gt;

&lt;h1 id=&quot;amdahls-law&quot;&gt;Amdahl’s Law&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Gene_Amdahl&quot;&gt;Gene Amdahl&lt;/a&gt; studied parallelization extensively. He was a
computer scientist who worked with mainframe computers at IBM and later his own companies, including
Amdahl Corportation (who made supercomputers).&lt;/p&gt;

&lt;p&gt;He gave us a useful observation, represented as this equation:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-parallel-efforts/amdahl-equation.png&quot; alt=&quot;the equation for Amdahls Law&quot; title=&quot;Amdahl&apos;s Equation&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;
&lt;figcaption class=&quot;text-center&quot;&gt;Amdahl&apos;s equation.&lt;/figcaption&gt;

&lt;ul&gt;
  &lt;li&gt;S is the speedup you can expect&lt;/li&gt;
  &lt;li&gt;(p) the part of the process that can be sped up by working in parallel&lt;/li&gt;
  &lt;li&gt;(1-p) is the part of the process that cannot be made parallel&lt;/li&gt;
  &lt;li&gt;(s) is the factor of the speedup from going parallel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might seem a little elementary on the surface of it - you have the
process that’s not working parallel plus the parallel part. Hardly brilliant
and hardly shocking at first glance.&lt;/p&gt;

&lt;p&gt;But hold on a minute. There is more to think about.&lt;/p&gt;

&lt;p&gt;Provided that the part of the process we want to speed up (p) is a critical area
for performance, and that we have a meaningful speedup factor (s), and that we
don’t have significant complications to the rest of the process (1-p part), we
can see a measurable speedup by working in parallel.&lt;/p&gt;

&lt;p&gt;One would assume that splitting our team members up, each taking a part of the
program, and all working in parallel would be faster and result in software that
we can deliver sooner.&lt;/p&gt;

&lt;p&gt;That is not always how it happens, though. Let’s explore…&lt;/p&gt;

&lt;h1 id=&quot;maybe-its-not-parallel&quot;&gt;Maybe it’s not parallel?&lt;/h1&gt;

&lt;p&gt;The parallel part of the process begins when we launch the parallel processors,
or in our case when we assign the parts of the work to our developers.&lt;/p&gt;

&lt;p&gt;It ends when the &lt;strong&gt;last&lt;/strong&gt; of the parallel parts complete.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2021-scatter-gather/not-parallel.png&quot; alt=&quot;A piece of work being split into three parallel tracks, with work being done at different times.&quot; title=&quot;work distributed in time&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In most software development organizations, especially the ones struggling with slow delivery,
the work is not parallel, only asynchronous. The two are not the same.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Asynchronous work isn’t necessarily parallel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is examined in more detail in the blog post &lt;a href=&quot;/blog//scatter-gather&quot;&gt;Scatter-Gather&lt;/a&gt; also on this site.&lt;/p&gt;

&lt;p&gt;If any parts of the divided job are delayed or interrupted, &lt;em&gt;that delay is included&lt;/em&gt; and extends
the “parallel part” of the process.&lt;/p&gt;

&lt;p&gt;What if the individuals are on different teams (perhaps a front-end and a
back-end team) and each of those teams has different priorities? You can imagine
if team A starts the work right away and finishes their big, but team B has
more pressing concerns and doesn’t start their portion for a few weeks. This
asynchrony is not parallel, so the overall time to completion is worsened.&lt;/p&gt;

&lt;p&gt;The individuals don’t have to be on different teams for parallelism to be broken.
It often happens that individuals have personal work queues, making it unlikely
that their asynchronous tasks will coincide.&lt;/p&gt;

&lt;p&gt;The higher the individual utilization, the more likely at least one “parallel” task will
queue and wait, swallowing up any speed gains.&lt;/p&gt;

&lt;p&gt;Consider a typical software development process where there are quality gates 
that may cause work to flow back to developers. If the work returns, then 
the task’s time expands. Backflow of work is a primary source of unpredictability
and in this case it can consume any intended time savings.&lt;/p&gt;

&lt;p&gt;What is the pass-rate, or &lt;a href=&quot;/blog/first-time-through/&quot;&gt;First-Time Through&lt;/a&gt; rate, for your quality pipeline?&lt;sup id=&quot;fnref:passrate&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:passrate&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
What is the turn-around time from when the work is submitted the first time 
until it is ready for integration?&lt;/p&gt;

&lt;p&gt;For any of the above reasons (or dozens of others) the time from assignment
until the &lt;em&gt;final&lt;/em&gt; completion of the &lt;em&gt;final&lt;/em&gt; part may stretch out in time and
consume any advantage we may have gained from allegedly-parallelizing the tasks.&lt;/p&gt;

&lt;h1 id=&quot;the-non-parallel-part&quot;&gt;The Non-parallel Part&lt;/h1&gt;

&lt;p&gt;The equation treats the non-parallel part &lt;em&gt;(1-p)&lt;/em&gt; as a single value, so one
might not see that it includes extra work that &lt;em&gt;preceeds&lt;/em&gt; and &lt;em&gt;follows&lt;/em&gt; the parallel work.&lt;/p&gt;

&lt;p&gt;In order to divide work up among developers, we have to have a pretty solid
plan in place. The plan must be complete enough to ensure that these independently-built parts will all fit
together at the end of the process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-parallel-efforts/splitting-and-joining-parallel-tasks.png&quot; alt=&quot;an image showing prework preceding parallel work, and re-integration work following parallel work&quot; title=&quot;overhead exists; this isn&apos;t free&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Prework involves getting a design that is complete enough to be portioned, breaking it into
sufficiently complete tasks to be done in parallel, assigning the work to be done&lt;sup id=&quot;fnref:bestresource&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:bestresource&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, and then (later)
inspecting, assembling, and testing the finished work.&lt;/p&gt;

&lt;p&gt;It may also involve marshalling or scheduling all the people who are to do the work, who may
otherwise not be available to do it in parallel.&lt;/p&gt;

&lt;p&gt;If the time for &lt;em&gt;prework&lt;/em&gt; plus the time for &lt;em&gt;parallel work&lt;/em&gt; plus the time to &lt;em&gt;integrate and finish&lt;/em&gt;
the work takes at least as long as it would have taken the available staff to do the work synchronously,
then there is &lt;strong&gt;no speed advantage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This happens more often than one might think.&lt;/p&gt;

&lt;h1 id=&quot;managing-parallel-work-is-harder&quot;&gt;Managing Parallel Work Is Harder&lt;/h1&gt;

&lt;p&gt;When work is divided, someone has to track it to make sure the prework is done, the parallel
tasks are all being completed well, and that the integration is progression well at the end.&lt;/p&gt;

&lt;p&gt;These steps require additional effort from the managers of the work, partly because the status
of the job is fragmented and distributed.&lt;/p&gt;

&lt;p&gt;It’s also a larger management burden because certain conditions must be met before the
work can be reliably assigned for parallel development (cf “Definition of Ready”) and
integration cannot be completed until all the parallel parts have finished.&lt;/p&gt;

&lt;p&gt;Hopefully all the individual pieces will integrate cleanly and easily&lt;sup id=&quot;fnref:hopeful&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:hopeful&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;But also, be aware that sometimes there may be dependencies between the jobs that would
otherwise be parallel. Watching for these, resolving them quickly, and preventing them
through clever design requires qttention and skill.&lt;/p&gt;

&lt;h1 id=&quot;how-can-we-win-this&quot;&gt;How Can We Win This?&lt;/h1&gt;

&lt;p&gt;We need only a few conditions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A low-cost, low-ceremony way to divide work&lt;/li&gt;
  &lt;li&gt;We need a low-cost way to integrate&lt;/li&gt;
  &lt;li&gt;We need to ensure the work is actually done in parallel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is hard for someone outside the development team to ensure these
conditions are met. It’s not impossible, but I’ve seen it fail and
degrade to asynchrony and extra work more often than not.&lt;/p&gt;

&lt;p&gt;When it works, it works a treat!&lt;/p&gt;

&lt;p&gt;Within the team it’s easier to make it work.&lt;/p&gt;

&lt;p&gt;It helps if team has limited work in progress (WIP),
and that the work to be done is assigned to the team as a whole rather
than parts of the work being assigned to individuals.&lt;/p&gt;

&lt;p&gt;Success is likely with front-end, back-end, UX, data services, reliability,
and cloud architecture all focused on the same feature at the same time.
This can happen even without pair programming or teaming.&lt;/p&gt;

&lt;p&gt;The trick is that they are communicating, adapting, and integrating all
simultaneously&lt;sup id=&quot;fnref:focus&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:focus&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;When integration is continuous, there is little non-paralellizable
work at the end of the period. This is possible when teams work in a
Trunk-based way, or at least share the same branch among all the people
working on a new feature.&lt;/p&gt;

&lt;p&gt;It is a low-ceremony, high-energy way of working that provides
quick results with lower incident of integration error or escaped
defects.&lt;/p&gt;

&lt;p&gt;You &lt;strong&gt;can&lt;/strong&gt; win this game. Amdahl’s equation gives you all the information
you need if you can apply it.&lt;/p&gt;

&lt;p&gt;Where it is hard to win, or rare to win, perhaps it is best to work in
a simple synchronous way and complete one task at a time.&lt;/p&gt;

&lt;h1 id=&quot;is-this-really-a-problem&quot;&gt;Is this really a problem?&lt;/h1&gt;

&lt;p&gt;If you aren’t accounting for the &lt;em&gt;additional&lt;/em&gt; non-parallel time and asynchrony,
it is likely that you have been losing the parallel game.&lt;/p&gt;

&lt;p&gt;A lot of people don’t consider the physics of the situation (as described
by Amdahl’s law) and blindly assume that asynchronous work with late integration
is the most efficient and speedy way to work. They lose this game continuously,
but don’t realize that they’re losing.&lt;/p&gt;

&lt;p&gt;There are definite engineering tradeoffs to be considered.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is your team too slow and unpredictable &lt;strong&gt;even though&lt;/strong&gt; you are doing the
maximum amount of work in parallel, or &lt;strong&gt;because&lt;/strong&gt; you are doing the work
asynchronously with extra overhead?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;that-explains-it&quot;&gt;That explains it!&lt;/h1&gt;

&lt;p&gt;Please consider the information here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/scatter-gather/&quot;&gt;Scatter Gather&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/faster-and-more-predictable/&quot;&gt;Faster And More Predictable&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/estimates-vs-actuals/&quot;&gt;Estimates and Actuals&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/pitfalls-of-solo-work/&quot;&gt;Pitfalls of Solo Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may want to &lt;a href=&quot;/get-in-touch/&quot;&gt;contact us&lt;/a&gt; for further training or coaching for Ways of Work
that are more likely to give you the outcomes you want and need.&lt;/p&gt;

&lt;h1 id=&quot;no-this-isnt-part-of-our-problem&quot;&gt;No, this isn’t part of our problem…&lt;/h1&gt;

&lt;p&gt;If you are not suffering the consequences of ill-considered
paralellization you may find there are ways to improve
your process by focusing on creating quality with superior
desk disciplines. Consider our Industrial Logic Ways of Work,
including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/first-time-through/&quot;&gt;First Time Through&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/clean-start-protocol&quot;&gt;The Clean Start protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/save-your-game&quot;&gt;Save Your Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/fast-feedback&quot;&gt;Fast Feedback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/reclaiming-700-hours&quot;&gt;Saving 700 hours a month&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many ways to focus improvement on having higher overall throughput
and satisfying the DORA metrics. &lt;a href=&quot;https://www.industriallogic.com/get-in-touch/&quot;&gt;We can discuss these with you&lt;/a&gt;, and
help you
make better software, sooner.&lt;/p&gt;

&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:divideandconquer&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Breaking up work this way is often erroneously described as “divide and conquer”. &lt;a href=&quot;#fnref:divideandconquer&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:passrate&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The pipeline’s pass rate is the product of individual gates’ pass rate. &lt;a href=&quot;#fnref:passrate&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:bestresource&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Often the person assigning the work goes to the extra work of dividing the work so that each task fits a specific person’s skill set (for efficiency’s sake). &lt;a href=&quot;#fnref:bestresource&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:hopeful&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;It is unlikely, since fragmented work is done by different people at different times, in different branches. &lt;a href=&quot;#fnref:hopeful&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:focus&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I’ve observed that software development is done most well and most quickly when we focus all of the relevant skills on the task at hand. &lt;a href=&quot;#fnref:focus&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 18 Feb 2025 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/parallel-efforts/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/parallel-efforts/</guid>
        
        <category>defects</category>
        
        <category>parallel tasks</category>
        
        <category>teamwork</category>
        
        <category>process improvement</category>
        
        <category>divide and conquer</category>
        
        <category>amdahl&apos;s law</category>
        
        
        <category>Agile Development</category>
        
        <category>Software Development</category>
        
        <category>Tech Safety</category>
        
      </item>
    
      <item>
        <title>Agentic Extract Method</title>
        <description>&lt;p&gt;Extract Method is one of the most commonly used automated refactorings. Many of us have been using it for well over two decades in tools like Eclipse and IntelliJ (and its family of IDEs). It’s a workhorse.&lt;/p&gt;

&lt;p&gt;And, while I appreciate it for what it can do, I’ve been tired of what it can’t do for a long time now. I wrote about this problem in a 2014 blog called &lt;a href=&quot;https://www.industriallogic.com/blog/improving-extract-method/&quot;&gt;Improving Extract Method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog, I’m going to show you how I used ChatGPT 03-mini and Junie (join the &lt;a href=&quot;https://plugins.jetbrains.com/plugin/26104-jetbrains-junie-eap&quot;&gt;EAP&lt;/a&gt; for this excellent tool) to perform a more intelligent Extract Method. The code we’ll look at is from the &lt;a href=&quot;https://github.com/emilybache/Parrot-Refactoring-Kata&quot;&gt;Parrot Kata&lt;/a&gt;, a code example from Martin Fowler’s Refactoring book, which was turned into a Kata (in many languages) by &lt;a href=&quot;https://bacheconsulting.com/&quot;&gt;Emily Bache&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code I want to refactor is the test code. As you can see below, for each test in ParrotTest, a Parrot instance is instantiated with specific values. As you can also see, there are three types of parrots (European, African and Norwegian):&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EUROPEAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_One_Coconut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_Two_Coconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_No_Coconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedNorwegianBlueParrot_nailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NORWEGIAN_BLUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedNorwegianBlueParrot_not_nailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NORWEGIAN_BLUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;18.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Parrot Kata is about introducing polymorphism into the code, and I have an approach to doing that which is driven outside-in, from the test code to the production code.&lt;/p&gt;

&lt;p&gt;I begin by extracting Parrot instantiation into its own creation method via the Extract Method automated refactoring. The trouble is, IntelliJ cannot do this the way I want. Here’s what it gives me when applying Extract Method on the first parrot instantiation of an African parrot:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_One_Coconut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_Two_Coconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_No_Coconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Notice how it did not change &lt;em&gt;all&lt;/em&gt; African parrot instantiations to use the new creation method? This happened because the other instantiations of African parrots happen to pass in different constructor values. What it generated wasn’t even close to what I wanted and therefore I did not even bother to rename it to createAfricanParrot(). The Extract Method refactoring has been incapable of figuring this out for way too long.&lt;/p&gt;

&lt;p&gt;It’s time for a change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extract Method with ChatGPT&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see how AI can help. Using ChatGPT 03-mini, I give it the ParrotTest source code and ask it to do the following:&lt;/p&gt;

&lt;p&gt;“In the ParrotTest, please extract a single creation method (a method that creates a Parrot instance) from all of the code that instantiates a Parrot class.”&lt;/p&gt;

&lt;p&gt;It does the work in seconds, giving me the following:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EUROPEAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_One_Coconut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; 
   &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I have one place in this test code where I instantiate parrots. It’s good, and I’m glad AI can produce this for me, but I actually want it to do more. What I want is one creation method for each parrot type.&lt;/p&gt;

&lt;p&gt;It can do that too. I begin with the original test code and ask ChatGPT the following:&lt;/p&gt;

&lt;p&gt;“Instead of making one createParrot method, can you make three: one for each ParrotTypeEnum that is instantiated?”&lt;/p&gt;

&lt;p&gt;It immediately does the work and gives me the following:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EUROPEAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createAfricanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createNorwegianBlueParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NORWEGIAN_BLUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_One_Coconut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createAfricanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedNorwegianBlueParrot_nailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createNorwegianBlueParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is exactly what I want at this stage in the Parrot Kata. Had this been client code or my own organization’s code, I may not have wanted to expose it to a public AI tool. Ideally, we could get similar AI help for Extract Method from directly within our IDE. That’s where Junie comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extract Methods with Junie&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Junie is a new AI plugin by JetBrains, currently in an Early Access Program. I’ve been playing with it and am really enjoying how it works. For this blog, I’ll show you how I interact with it to get help with Extract Methods.&lt;/p&gt;

&lt;p&gt;Inside of IntelliJ, I ask Junie to do the following:&lt;/p&gt;

&lt;p&gt;“In ParrotTest, create one Creation Method for each Parrot Type, passing in number of coconuts, voltage and isNailed, even if they aren’t needed.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-agentic-extract-method/extract-methods-with-junie.png&quot; alt=&quot;alt_text&quot; title=&quot;image_tooltip&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;
&lt;figcaption class=&quot;text-center&quot;&gt;Junie chat window inside of IntelliJ&lt;/figcaption&gt;

&lt;p&gt;After a few seconds, It gives me what I want:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EUROPEAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createAfricanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AFRICAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createNorwegianBlueParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ParrotTypeEnum&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NORWEGIAN_BLUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numberOfCoconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;voltage&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isNailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createEuropeanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;12.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_One_Coconut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createAfricanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getSpeedOfAfricanParrot_With_Two_Coconuts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Parrot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createAfricanParrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parrot&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSpeed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may wonder why I had to tell Junie what parameters to pass in to each Creation Method. Junie is clever. If I didn’t specify that, Junie would omit any parameters that it could simply hardcode into the constructor calls, if they happen to be the same values for all instantiations (e.g. a “false” value for isNailed).&lt;/p&gt;

&lt;p&gt;BTW, if and when Junie does something you don’t want, you can simply tell it to undo the work and it will gracefully retreat to the prior state of your code.&lt;/p&gt;

&lt;p&gt;So far, I’m loving how Junie performs. I’d encourage you to play with it by getting into the Early Access Program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Refactoring has traditionally been an atomic operation: you implement or execute one refactoring at a time. To complete the design change you want, this may involve performing a sequence of refactorings. I’ve been doing that for well over 2 decades now and frankly, I’m tired of it. I’m lazy and I simply want my refactoring tools to do more. Thanks to AI, that’s now possible.&lt;/p&gt;

&lt;p&gt;Today, I’m using a combination of automated + agentic refactoring when improving the design of code. While I can ask AI to perform very large steps, that tends to be more dangerous. So I’m asking it to perform slightly more powerful refactorings. And keep in mind that I could not and would not do this if I didn’t have thorough, automated checks to ensure that my changes preserve behavior. With or without AI, refactoring must be safe.&lt;/p&gt;

&lt;p&gt;The use of AI in refactoring is a rich new area, changing rapidly. If we use it wisely, it can help us improve the design of code more efficiently and effectively. I for one am overjoyed that I am no longer stuck with the limited functionality in the available set of automated refactoring tools. AI has the potential to help us make more intelligent design transformations. We just need to make sure that these transformations are safe.&lt;/p&gt;
</description>
        <pubDate>Mon, 17 Feb 2025 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/agentic-extract-method/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/agentic-extract-method/</guid>
        
        <category>refactoring</category>
        
        <category>AI</category>
        
        <category>Software Design</category>
        
        
        <category>Refactoring</category>
        
      </item>
    
      <item>
        <title>Fast Feedback</title>
        <description>&lt;h2 id=&quot;series-index&quot;&gt;Series index&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/clean-start-protocol/&quot;&gt;Clean Start Protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/fast-feedback/&quot;&gt;Fast Feedback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/easy-integration/&quot;&gt;Easy Integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/safe-release/&quot;&gt;Safe Releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-fast-feedback/top-image.png&quot; alt=&quot;A picture of the five-step discipline, hightlighting fast feedback&quot; title=&quot;Otherwise, why waste our time?&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We’ve covered the &lt;a href=&quot;https://www.industriallogic.com/blog/clean-start-protocol/&quot;&gt;Clean Start protocol &lt;/a&gt;and how to make
for &lt;a href=&quot;https://www.industriallogic.com/blog/save-your-game/&quot;&gt;Easy Recovery&lt;/a&gt; already, and it’s time for us to discuss Fast
Feedback.&lt;/p&gt;

&lt;p&gt;When someone in engineering talks about quality, they usually mean that the item in question&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;performs a useful function&lt;/li&gt;
  &lt;li&gt;does so reliably&lt;/li&gt;
  &lt;li&gt;has low cost&lt;/li&gt;
  &lt;li&gt;has easy maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have many arguments in social media where people take the word “quality” in a different sense, to imply aesthetic
purity, opulence, luxury, and outrageous lists of features.&lt;/p&gt;

&lt;p&gt;When we talk about feedback, we take the engineering sense of quality. We want the product that we deliver to fulfill
useful functions reliably and affordably, with easy maintenance.&lt;/p&gt;

&lt;p&gt;In order the product we produce fulfills our intentions we need to measure our effectiveness with feedback.&lt;/p&gt;

&lt;p&gt;We don’t want to operate from unchecked self-confidence alone (although apparently &lt;a href=&quot;https://gousios.org/pub/developer-testing-in-IDE.pdf&quot;&gt;some do&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To this end, we need both in-session feedback and external feedback. Let’s take an inside-out tour of this important
topic.&lt;/p&gt;

&lt;h1 id=&quot;in-session-feedback&quot;&gt;In-Session Feedback&lt;/h1&gt;

&lt;p&gt;Programmers work within ambitious schedules, even when they set their own. This often motivates them to work in big
batches and huge steps when a smaller, more measured approach is preferable.&lt;/p&gt;

&lt;p&gt;Working in small steps isn’t a virtue unto itself, but rather enables us to have better feedback loops to maintain and
improve the product’s quality and our productivity.&lt;/p&gt;

&lt;p&gt;The tight “inner loop” of software development is the interaction of the programmer(s) and the IDE when creating code.&lt;/p&gt;

&lt;p&gt;Consider the life cycle of a defect:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-fast-feedback/defect-lifecycle.png&quot; alt=&quot;a drawingshowing period A from creating a defect to detecting it, and period B from detection to correction&quot; title=&quot;Where baby errors come from&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;
&lt;figcaption class=&quot;text-center&quot;&gt;Period a governs period b&lt;/figcaption&gt;

&lt;p&gt;A defect is &lt;em&gt;injected&lt;/em&gt; when some programming decision is made in the codebase or its dependencies that does not pan out
for all the uses of that code.&lt;/p&gt;

&lt;p&gt;Some defects are “shallow”; they are easy to fix now, and will still be easy to fix a year from now.&lt;/p&gt;

&lt;p&gt;Some defects are “deep”; they are hard to reproduce, and understand, and occasionally hard to fix or work around. It may
take many hours or days to figure out what behavior in the code causes defective behavior, which is then remedied in a
few lines or characters of code. Some of us have had the unpleasant job of explaining why our multi-day struggle
resulted in changing such little code&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;In general, period A governs period B.&lt;/p&gt;

&lt;p&gt;If (after a clean start) I’ve written only one line of code before the alarm bells go off, I know I have only one line
of code to examine. The error can not be anywhere else.&lt;/p&gt;

&lt;p&gt;If I undo the change immediately, everything returns to its original function with no errors or failing tests.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The larger the set of changes before the defect is found, the more work it takes to discover what is wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is painfully true if changes from multiple authors are all merged and only then the newly composed application is
found to have a logical error.&lt;/p&gt;

&lt;p&gt;I want to know whether every change is valid, and I want to know within seconds.&lt;/p&gt;

&lt;p&gt;This is the goal of what we call &lt;strong&gt;&lt;em&gt;continuous testing&lt;/em&gt;&lt;/strong&gt;. We want some element of our process to surface errors as soon
as they occur.&lt;/p&gt;

&lt;p&gt;To support continuous testing, some IDEs provide a feature to rerun the tests after every code change.&lt;/p&gt;

&lt;p&gt;Sometimes auto-testers are included with a language’s unit testing framework, running the tests whenever a file has been
saved. Often these run in a separate window. Most programmers have multiple screens or at least enough screen real
estate to have multiple windows open and visible at once, so this is “good enough.”&lt;/p&gt;

&lt;p&gt;Feedback loops &lt;em&gt;in the editing session&lt;/em&gt; can be either one of two things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fast&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;In&lt;/strong&gt;effective.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no room for slow checks and tests in the tight inner loop of coding.&lt;/p&gt;

&lt;p&gt;Developers will run slow tests less often and do more work between checks. This increases period A, and period B (
searching for causes of defects) naturally expands.&lt;/p&gt;

&lt;p&gt;If the tests are too slow, hurried developers will disable them.&lt;/p&gt;

&lt;p&gt;We can do better than that.&lt;/p&gt;

&lt;p&gt;We have several tools at our disposal to help:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/solving-the-puzzle-at-the-center-of-successful-remote-mob-programming/&quot;&gt;We work together&lt;/a&gt;
to produce code, which not only helps us spot any mistyped code and logical errors, but we spot and correct thinking
errors before we even type the code – preemptively. That’s faster than “immediate.”&lt;/li&gt;
  &lt;li&gt;Any good IDE has &lt;strong&gt;&lt;em&gt;syntax highlighting&lt;/em&gt;&lt;/strong&gt; to spot common spelling and punctuation errors. This is immediate. I use a
lot of JetBrains editors, and I respond to warnings (shown as a yellow triangle in the upper-right corner). I try to
have zero warnings and zero errors.&lt;/li&gt;
  &lt;li&gt;Most editors support an &lt;strong&gt;&lt;em&gt;error checker (“linter”)&lt;/em&gt;&lt;/strong&gt; that will spot common code issues (“code smells”), security
vulnerabilities, and potential errors. I use SonarLint in most of my editors and recommend doing likewise.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;&lt;a href=&quot;https://www.geepawhill.org/2018/04/16/the-technical-meaning-of-microtest/&quot;&gt;Microtests&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt; ensure that the various
functions and classes still work as we intended. This feedback allows us to refactor the code freely into any other
form that passes the same tests&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;. The period of these tests is sub-one-minute, often less than 10 seconds, so we
use these with an “auto tester” that runs the tests whenever the code changes or a file is saved.&lt;/li&gt;
  &lt;li&gt;We often have &lt;strong&gt;&lt;em&gt;suites of integration, component, or large unit tests&lt;/em&gt;&lt;/strong&gt;. These take longer to run, generally but
sometimes fast enough is fast enough. If they are too slow for the continuous testing part of our workflow, we can
still run them periodically, at least every 20 or 30 minutes.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;&lt;em&gt;Pre-commit hooks&lt;/em&gt;&lt;/strong&gt; are often used to run slower tests before a change is allowed to be made permanent.&lt;/p&gt;

    &lt;div class=&quot;blog-text--blue&quot;&gt;The faster the tests are, the more useful they are for continuous testing and refactoring.&lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We use &lt;a href=&quot;https://www.industriallogic.com/blog/tdd-purposes-and-practices/&quot;&gt;TDD&lt;/a&gt; as a way to establish microtests so that  when we write any line of code,
we &lt;a href=&quot;https://www.industriallogic.com/blog/do-they-hate-writing-tests/&quot;&gt;already have test coverage&lt;/a&gt; and can immediately refactor it safely.&lt;/p&gt;

&lt;p&gt;Teaming, continuous testing, TDD, and a well-configured IDE give us the safety we need to apply CI &amp;amp; CD.&lt;/p&gt;

&lt;p&gt;Of course, all of this is in service of writing and refactoring the code so we deliver a solution with low maintenance
costs and high &lt;a href=&quot;https://www.industriallogic.com/blog/code-virtues-explained/&quot;&gt;code virtues&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;external-feedback&quot;&gt;External Feedback&lt;/h1&gt;

&lt;p&gt;I listed the in-session feedback first not because it’s the most important for the success of a project, but because
it’s the feedback that most of the readership will engage in the most often. It’s important to build the thing right and
to move quickly with changes so that our external feedback can be fast, too.&lt;/p&gt;

&lt;p&gt;Imagine that you’re rolling up a yoga mat. The inner diameter of your first roll has a huge effect on the outer diameter
of the fully-rolled mat.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-fast-feedback/rolled-mat.png&quot; alt=&quot;photo of a tigthly rolled-up exercise mat&quot; title=&quot;A tight inner roll keeps the outer diameter small&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;
&lt;figcaption class=&quot;text-center&quot;&gt;Outer diameter is constrained by inner diameter&lt;/figcaption&gt;

&lt;p&gt;In the same way, if development takes weeks or months, we can’t get user feedback very often. Quick
deployments lead to faster feedback for companies willing to capitalize on the opportunity.&lt;/p&gt;

&lt;p&gt;So there are a few different things to offer here.&lt;/p&gt;

&lt;p&gt;Quality and speed won’t necessarily lead to success. Products tend to die either for reasons of internal politics or
because they fail to achieve market fit.&lt;/p&gt;

&lt;p&gt;User Experience disciplines have goal-seeking feedback mechanisms. Some can be applied prior to beginning development.
Interviews, research, user persona, MVPs, – it’s an important skill set for ensuring that you are creating a solution
that might have a market fit.&lt;/p&gt;

&lt;p&gt;The idea of “plan it all up front” has a decidedly poor history due to its “all or nothing” bests on the first product
design chosen. Incremental efforts have about a 40% higher chance of success.&lt;/p&gt;

&lt;p&gt;It is not enough to be “right to begin with.” We have to adjust on-the-fly to the changes in needs and expectations if
we wish to remain viable and competitive.&lt;/p&gt;

&lt;p&gt;Not only are software solutions dealing with wicked problems (multiple stakeholders, different intentions and needs,
different perspectives, etc) but also providing a solution can change the nature of the problem.&lt;/p&gt;

&lt;p&gt;Given a good way to collect payments, the shop suddenly finds that they need financial analysis, costing based on
materials and labor, and business forecasting based on the payment data. In this way, an available solution uncovers or
creates new needs.&lt;/p&gt;

&lt;p&gt;When touchscreen devices became commonplace people suddenly “needed” mobile apps to do things they used to do on
desktop computers. This need was generated by changes in the larger consumer electronics market not from any direct
forces.&lt;/p&gt;

&lt;p&gt;Thankfully, UX doesn’t end at initial analysis and planning, but continues with the Build-Measure-Learn cycles and the
whole toolbox of product evolution techniques.&lt;/p&gt;

&lt;p&gt;We need the development teams to provide rapid build/release cycles to support the UX efforts to find and improve our
products’ market fit.&lt;/p&gt;

&lt;p&gt;This relates back to our defect lifecycle diagram (above): how can we shorten the period between making a product
decision and finding out if it makes a positive difference?&lt;/p&gt;

&lt;h1 id=&quot;does-it-matter&quot;&gt;Does it matter?&lt;/h1&gt;

&lt;p&gt;Not every company needs or wants to be nimble and experimental. You might have a job that doesn’t have any interest in
the speed, quality, or market fit for your work. That might actually be quite nice.&lt;/p&gt;

&lt;p&gt;If you work for a company that is not interested in requesting feedback or acting upon it, evidenced by making no move
to enable it, then this may be wasted effort.&lt;/p&gt;

&lt;p&gt;On the other hand, the defect life cycle is affecting you. If you have to spend time remediating bugs via debugging, or
loading the application with print/log statements, or bisecting commit histories and various other “extreme sports” of
the programming field then you may find immediate relief and value in using various technical feedback techniques like
the ones described here.&lt;/p&gt;

&lt;p&gt;It might make your work easier and more fun. Who doesn’t like that?&lt;/p&gt;

&lt;!-- Footnotes themselves at the bottom. --&gt;

&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;My colleague had cast a C “long” through a “short” integer in one case. It was easy to understand, hard to find. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;See &lt;a href=&quot;https://medium.com/pragmatic-programmers/unit-tests-are-first-fast-isolated-repeatable-self-verifying-and-timely-a83e8070698e&quot;&gt;Unit Tests Are First&lt;/a&gt; for more on this topic. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I won’t say that TDD improves design. it lets me change design freely, so improvement is always a possible choice. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 13 Feb 2025 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/fast-feedback/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/fast-feedback/</guid>
        
        <category>feedback</category>
        
        <category>test speed</category>
        
        <category>auto-testers</category>
        
        <category>sonarlint</category>
        
        <category>quality</category>
        
        <category>defects</category>
        
        <category>bugs</category>
        
        <category>debugging</category>
        
        <category>continuous testing</category>
        
        <category>user experience</category>
        
        
        <category>Agile Development</category>
        
        <category>Continuous Delivery</category>
        
        <category>Continuous Integration</category>
        
        <category>Extreme Programming</category>
        
        <category>Mob Programming</category>
        
        <category>Pair Programming</category>
        
        <category>Process Improvement</category>
        
        <category>Software Development</category>
        
        <category>Tech Safety</category>
        
      </item>
    
      <item>
        <title>The ROI of Saving 700+ Hours Per Month</title>
        <description>&lt;h2 id=&quot;how-teams-waste-700-hoursmonth&quot;&gt;How Teams Waste 700+ Hours/Month&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;By&lt;/em&gt; Wyatt Sutherland&lt;/p&gt;

&lt;p&gt;Time is the most valuable resource for any software development team. Yet, despite the best intentions, teams lose over 700 hours per month—17+ weeks— to inefficiencies, unnecessary meetings, redundant processes, and outdated workflows.&lt;/p&gt;

&lt;p&gt;If you’re feeling the strain of tight deadlines and overloaded sprints, you’re not alone. The good news? You can reclaim those lost hours—without hiring more staff or working overtime.&lt;/p&gt;

&lt;h3 id=&quot;the-shocking-truth-about-your-teams-time&quot;&gt;The Shocking Truth About Your Team’s Time&lt;/h3&gt;

&lt;p&gt;Many development teams unknowingly and habitually waste hundreds of hours each week on tasks that don’t contribute directly to delivering value. Here are some of the biggest culprits:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Unnecessary Meetings&lt;/em&gt;: Team members spend an average of 32 hours per month in meetings which could have been achieved with short sync-ups and improved &lt;strong&gt;ways of working (WoW)&lt;/strong&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Manual Processes&lt;/em&gt;: Repetitive tasks, such as code reviews, deployments, and regression testing, take hours to complete, while better working methods or automation could handle them in minutes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Poor Workflows&lt;/em&gt;: Quality gates catch issues too late in the development cycle, resulting in significant rework, and overhead, costing your organization at least twice as much and often more.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Lack of Collaboration&lt;/em&gt;: Blockers, insufficient requirements, unknown acceptance criteria, handoffs, waiting on others, and unclear communication channels lead to confusion and costly delays.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;Context Switching&lt;/em&gt;: Constantly jumping between tasks can reduce productivity by up to 40%, costing hours of lost focus daily.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;how-to-get-those-700-hours-back&quot;&gt;How to Get Those 700 Hours Back&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.industriallogic.com/get-in-touch/&quot;&gt;We at IL are a pragmatic group&lt;/a&gt; and know that reclaiming those lost hours doesn’t require drastic changes. Instead, we recommend a series of strategic adjustments that can yield significant time savings. Here are some simple recommendations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;h4 id=&quot;subdue-the-meeting-monster&quot;&gt;Subdue the Meeting Monster&lt;/h4&gt;

    &lt;p&gt;Evaluate your meeting schedule and eliminate or consolidate unnecessary gatherings. Use async communication tools like Slack, Teams, Loom, &lt;a href=&quot;https://www.industriallogic.com/blog/discovery-trees/&quot;&gt;Discovery Trees&lt;/a&gt;, or project management platforms to replace long status meetings with concise updates. At IL we use Slack and Discovery Trees for our initiatives and apply The Law of Two Feet (leave meetings) if we’re not learning, or contributing.&lt;/p&gt;

    &lt;p&gt;Quick Win: Implement a “No-Meeting Wednesday” policy and watch productivity soar. After 3-4 weeks, add another No-Meeting Day. We find this approach highly effective and beneficial to our clients.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h4 id=&quot;automate-everything-possible&quot;&gt;Automate Everything Possible&lt;/h4&gt;

    &lt;p&gt;Leveraging automation tools for everything from CI/CD pipelines to automated testing and code formatting can drastically reduce manual workload and free up hours for meaningful work.&lt;/p&gt;

    &lt;p&gt;Quick Win: Use tools like Jenkins, CircleCI, or GitHub Actions to automate your build and deployment processes. Use an IDE with solid refactoring tools and use the tools. Too often we observe folks manually refactoring when their IDE is faster and not as error-prone as a human.&lt;/p&gt;

    &lt;p&gt;We’ve found in-IDE scanners like SonarLint help us find common issues and vulnerabilities during our code-editing sessions. These tools usually will not only mark the problem but also explain it so you can avoid it down the line.&lt;/p&gt;

    &lt;p&gt;Useful automation doesn’t have to be an expensive proposition. We get frequent small boosts in speed using simple tools like the &lt;em&gt;gnext&lt;/em&gt; and &lt;em&gt;gdone&lt;/em&gt; functions from &lt;a href=&quot;https://www.industriallogic.com/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt; or the prepare script described in &lt;a href=&quot;https://www.industriallogic.com/blog/clean-start-protocol/&quot;&gt;The Clean Start Protocol&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h4 id=&quot;optimize-workflows&quot;&gt;Optimize Workflows&lt;/h4&gt;

    &lt;p&gt;At IL, we believe building in quality early into your process is a critical step to achieve as soon as possible. You’ll want to emphasize shift-left testing during the coding phase for improved software quality, better tests and coverage, and faster time to market. This approach will reduce overlapping redundant tests usually found in teams that have a separate testing unit, and shift-left testing will help get the software to production with fewer escaped defects.&lt;/p&gt;

    &lt;p&gt;With many of our clients, we have seen test engineers writing code and in some cases, clients have added testers as full-time team members and eliminated their testing department.&lt;/p&gt;

    &lt;p&gt;Quick Win: Ask your testers to collaborate with the developers closely during design and development.&lt;/p&gt;

    &lt;p&gt;Gamify Testing: Challenge testers and developers to compete in fixing defects during the coding phase using leaderboards, rewards, or recognition to reduce defects found in the QA phase.  HINT: Look into Stryker for automating your mutation tests to see insufficient test coverage.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h4 id=&quot;increase-collaboration&quot;&gt;Increase Collaboration&lt;/h4&gt;

    &lt;p&gt;Another big source of wasted time is &lt;a href=&quot;https://www.industriallogic.com/blog/faster-and-more-predictable/&quot;&gt;waiting on dependencies&lt;/a&gt;. Instead of simply managing these dependencies, focus on identifying your value stream partners. Rather than handing tasks off to another team or waiting for them—much like the principles of shift-left testing—aim to encapsulate your dependencies and work collaboratively through ensemble development.&lt;/p&gt;

    &lt;p&gt;If you’ve not experimented with ensemble development we recommend that you try. There’s a sweet spot ensemble size and it depends on the context. We’ve written many blogs on this modern practice. &lt;a href=&quot;https://www.industriallogic.com/blog/&quot;&gt;Visit IL’s blog posts to learn more&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h4 id=&quot;reduce-context-switching&quot;&gt;Reduce Context Switching&lt;/h4&gt;

    &lt;p&gt;Each switch costs time—often 15-30 minutes per switch, which adds up quickly.&lt;/p&gt;

    &lt;p&gt;Dedicate specific time blocks for deep mindful work and make sure folks know it’s non-interruptable time. This allows your brain to stay engaged in one type of task at a time.&lt;/p&gt;

    &lt;p&gt;Limit your Work in Progress (WIP) by focusing on fewer tasks at once. At IL we use a Kanban/Swarm board and WIP limits. &lt;a href=&quot;https://www.industriallogic.com/blog/managing-interruptions/&quot;&gt;Setting WIP limits will help avoid overloading simultaneous tasks and reduce interruptions&lt;/a&gt;; this approach requires discipline so try not to let the old habits creep back in. We also practice taking a short break about every 50-60 minutes so our thinking remains fresh and uncluttered.&lt;/p&gt;

    &lt;p&gt;Quick Win: Similar to “Subdue the Meeting Monster,” set aside a portion of each day where the entire team works on focused, uninterrupted tasks. Start with 2-3 consecutive hours in the morning and see if you can extend the length over time. Even a single day of deep focus can significantly boost productivity and reinforce the habit of reducing context switching. Communicate your intent with your manager so they support your efforts across the org.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;where-did-700-hours-come-from&quot;&gt;Where Did 700 Hours Come From&lt;/h3&gt;

&lt;p&gt;Seven hundred hours might sound like an exaggeration, but when you break it down, it’s actually a conservative estimate for a seven-member team—a product manager, a designer, and a mix of developers and test engineers—working in a traditional software development model.&lt;/p&gt;

&lt;p&gt;At IL, we refer to this traditional approach as &lt;a href=&quot;https://www.industriallogic.com/blog/scatter-gather/&quot;&gt;Scatter-Gather&lt;/a&gt;—where engineers receive their assignments, work independently in silos, and then come together later to integrate their code into the larger system. While this approach might feel comfortable, it introduces inefficiencies at nearly every stage of development.&lt;/p&gt;

&lt;p&gt;We’ve worked with hundreds of companies, and the vast majority still operate in a Scatter-Gather model without fully examining the hidden costs of their workflow. So, we did the math.&lt;/p&gt;

&lt;p&gt;Our calculator accounts for time spent on Non-Value Tasks (NVTs)—things like coordinating meetings, knowledge sharing sessions, manual regression testing, integrating work, handling pull requests, hardening sprints, waiting on blockers, acceptance criteria validation, design clarifications, triage and bug fixing, etc.&lt;/p&gt;

&lt;p&gt;For this blog’s example, we analyzed a team’s average weekly overhead, calculated as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Each team member spends on average &lt;strong&gt;28 hours per week&lt;/strong&gt; on NVTs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The team on average completes &lt;strong&gt;10 features/stories/tasks per week&lt;/strong&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Work goes through &lt;strong&gt;5 quality gates&lt;/strong&gt;, with a &lt;strong&gt;90% pass rate at each gate&lt;/strong&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Due to this compounding failure rate, &lt;strong&gt;41% of tasks are returned for rework&lt;/strong&gt; (calculated as 1-(90%^5)).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Each returned task requires &lt;strong&gt;3 hours of rework&lt;/strong&gt; on average.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;That results in 4.1 returned tasks per week, leading to 13 additional hours of rework per week across the team.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Total weekly overhead in a traditional model:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;Non-value tasks: 28 × 7 = 196 hours&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Rework due to returned tasks: 13 hours&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Total: 209 hours per week&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, scaling up to a month:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The average number of weeks per month is 4.33.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Total monthly overhead: 209 × 4.33 ≈ 900 hours.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-impact-of-an-optimized-approach&quot;&gt;The Impact of an Optimized Approach&lt;/h3&gt;

&lt;p&gt;In contrast, teams that shift to optimized ways of working &lt;strong&gt;drastically reduce this overhead&lt;/strong&gt;. When teams implement a streamlined workflow that minimizes handoffs, reduces waiting time, and improves collaboration, the average &lt;strong&gt;weekly overhead drops to 43 hours per team and better.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Optimized WoW total monthly overhead:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;43 × 4.33 = 186 hours per month&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Total waste prevented per month:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;900 (traditional WoW) - 186 (optimized WoW) = 714 hours saved per month.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Rounding adjustments bring this to 719 hours of recovered productivity.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-hidden-costs-of-traditional-development&quot;&gt;The Hidden Costs of Traditional Development&lt;/h3&gt;

&lt;p&gt;Using real-world data, we built the &lt;strong&gt;Way of Working – Waste Cost Value Calculator&lt;/strong&gt; to quantify inefficiencies.&lt;/p&gt;

&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;

&lt;p&gt;Data doesn’t lie. Across teams we’ve analyzed, the cumulative impact of these inefficiencies &lt;strong&gt;exceeds 700 hours per month&lt;/strong&gt;—the equivalent of nearly 18 full-time workweeks or hiring four additional full-time developers. But instead of throwing more people at the problem, the real solution lies in optimizing how work gets done.&lt;/p&gt;

&lt;p&gt;By shifting to more streamlined ways of working, teams reclaim lost time, ship higher-quality software, and achieve more—without overtime, burnout, or budget increases.&lt;/p&gt;

&lt;h3 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;/h3&gt;

&lt;p&gt;Reclaiming 700 hours per month isn’t a fantasy—it’s achievable with the right approach. By eliminating waste, automating tasks, and optimizing workflows, your product delivery team can focus more on innovation and delivering value rather than getting bogged down by inefficiencies.&lt;/p&gt;

&lt;p&gt;Want to put these strategies into action? Start small, measure progress, and watch your team’s productivity skyrocket.&lt;/p&gt;

&lt;h3 id=&quot;waste-cost-value-calculator-traditional-vs-il-wow&quot;&gt;Waste Cost Value Calculator (Traditional vs. IL WoW)&lt;/h3&gt;

&lt;h4 id=&quot;cost-efficiency-gains-with-il-wow-a-summary&quot;&gt;Cost Efficiency Gains with IL WoW: A Summary&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-02-reclaiming-700-hours/roi-summary.png&quot; alt=&quot;an image of the ROI summary&quot; title=&quot;Cost Efficiency Gains:ROI Summary&quot; class=&quot;square-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This section showcases the substantial time and financial savings a seven-member team can achieve by shifting from a traditional WoW to an IL WoW. The calculations assume a team composed of a product manager, a designer, and a mix of developers and test engineers.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;719 hours saved per month – Optimized workflows and reduced inefficiencies free up valuable development time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$77,328 monthly overhead cost – The financial burden of inefficiencies in traditional WoW.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$1.16M annual overhead cost (traditional WoW) – The yearly cost of maintaining outdated processes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$240K annual overhead cost (IL WoW) – A dramatic reduction in waste after IL WoW adoption.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;79% reduction in annual operating cost – A significant efficiency gain per team.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$927,940 in annual savings: The projected cost reduction after one year of IL WoW implementation.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This breakdown highlights the massive potential for teams to improve efficiency, lower costs, and boost productivity by embracing modern, optimized ways of working.&lt;/p&gt;

&lt;h4 id=&quot;from-waste-to-efficiency-cutting-overhead-with-il-wow&quot;&gt;From Waste to Efficiency: Cutting Overhead with IL WoW&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-02-reclaiming-700-hours/impact-of-waste.png&quot; alt=&quot;an image showing the impact of non-value tasks&quot; title=&quot;Impact of non-value tasks&quot; class=&quot;square-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This section highlights the time and financial costs associated with non-value-added tasks (NVT), delays, and inefficiencies in traditional workflows vs IL WoW.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;209 hours of overhead per week → 43 hours per week: Teams operating under traditional workflows lose an average of 209 hours per week to inefficiencies. By adopting IL WoW, this is reduced to just 43 hours—a dramatic improvement.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;75% of working hours lost → 15% lost: Under traditional WoW, 75% of available time is consumed by overhead, compared to just 15% when IL WoW is implemented.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;$22,468 overhead cost per week → $4,623 per week: The financial burden of wasteful processes is significantly reduced, cutting weekly overhead costs by nearly 80%.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This data underscores how inefficient workflows eat away at productivity and budgets. By addressing waste, teams can reclaim valuable time and resources for more impactful work.&lt;/p&gt;

&lt;h4 id=&quot;the-hidden-cost-of-rework-quality-control-inefficiencies&quot;&gt;The Hidden Cost of Rework: Quality Control Inefficiencies&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-02-reclaiming-700-hours/returned-redo-work.png&quot; alt=&quot;an image of the hidden cost of rework&quot; title=&quot;Hidden cost of rework from quality control gates.&quot; class=&quot;square-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This section highlights how reducing unnecessary quality gates minimizes rework, streamlines processes, and accelerates delivery. By cutting down on redundant reviews and revisions, teams can improve efficiency, reduce wasted effort, and focus on delivering higher-value work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️ Tip: &lt;em&gt;Build quality in up front rather than relying primarily on downstream verification&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id=&quot;traditional-ways-of-working&quot;&gt;Traditional Ways of Working:&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Teams pass through five quality gates, each requiring approvals, reviews, and handoffs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Despite a 90% pass rate, 41% of work still requires rework, adding 13 hours of returned work per week per team.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Multiple gates cause delays and waiting, leading to inefficiencies and bottlenecks.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;il-ways-of-working&quot;&gt;IL Ways of Working:&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The team works in an ensemble model, collaborating throughout development, eliminating the need for multiple gates.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Only one quality gate remains—customer or stakeholder approval when the product goes live.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With a 95% pass rate, only 5% of work requires rework, reducing overhead to just 1 hour per week per team.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;This shift ensures that work is completed correctly in real time, avoiding unnecessary handoffs, approvals, and bottlenecks.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adopting IL WoW, teams eliminate redundant approval steps, reduce delays, and significantly cut rework time, leading to faster delivery and higher-quality outcomes.&lt;/p&gt;

&lt;h4 id=&quot;before--after-reducing-non-value-tasks-with-il-wow&quot;&gt;Before &amp;amp; After: Reducing Non-Value Tasks with IL WoW&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-02-reclaiming-700-hours/non-value-task-hours-per-week.png&quot; alt=&quot;calculation of costs before and after reducing waste&quot; title=&quot;Calculation of costs before and after reducing waste.&quot; class=&quot;square-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This section highlights the significant reduction in non-value tasks (NVTs) achieved through IL WoW. By minimizing time spent in meetings, waiting, knowledge transfer, manual regression testing, and integrating work, teams can focus on high-impact tasks that drive innovation and efficiency.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Coordinating Meetings: Teams in IL WoW spend an average of just 3 hours per week, typically in 10–15 minute daily sync-ups. The end-to-end value stream is embedded within the team, minimizing external dependencies and distractions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Time Spent Waiting &amp;amp; Knowledge Transfer: Whole-team ensemble development ensures continuous collaboration with zero to minimal interruptions, reducing workflow delays and unnecessary knowledge handoffs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Integrating Work: Trunk-based, small-batch development with frequent integrations (multiple times per day) eliminates the overhead of complex, large-scale merges.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Manual Regression Testing: IL WoW embraces XP development principles, with engineers practicing Test-Driven Development (TDD) to build comprehensive automated test suites. Discovery testing is limited to a handful of manual tests, while mutation testing is automated using tools like Stryker.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;want-to-calculate-wow-roi-with-your-team-or-group&quot;&gt;Want To Calculate WoW ROI with Your Team or Group?&lt;/h3&gt;

&lt;p&gt;We’d be happy to spend a half-hour with you on a call to explore your context and calculate potential savings from better Ways of Working.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.industriallogic.com/get-in-touch/&quot;&gt;CONTACT US&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Tue, 04 Feb 2025 01:31:16 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/reclaiming-700-hours/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/reclaiming-700-hours/</guid>
        
        <category>700 hours</category>
        
        <category>roi</category>
        
        <category>return on investment</category>
        
        <category>inefficiencies</category>
        
        <category>efficiencies</category>
        
        <category>ensemble programming</category>
        
        <category>mob programming</category>
        
        <category>quality gates</category>
        
        <category>overtime</category>
        
        <category>waste</category>
        
        <category>waste cost calculator</category>
        
        <category>ways of working</category>
        
        <category>dependencies</category>
        
        <category>value stream</category>
        
        <category>shift-left testing</category>
        
        <category>context switching</category>
        
        
        <category>Productivity</category>
        
        <category>Process Improvement</category>
        
        <category>Organizational Performance</category>
        
        <category>Culture</category>
        
        <category>Behavior Skills</category>
        
      </item>
    
      <item>
        <title>Gilded Rose with AI and PyCharm</title>
        <description>&lt;p&gt;This story comes from a software technical craft person of some standing who has never been an AI pundit or promoter.&lt;/p&gt;

&lt;p&gt;It seems to be helpful when incorporated with other good habits and managed respectfully.&lt;/p&gt;

&lt;p&gt;I used the &lt;a href=&quot;https://github.com/emilybache/GildedRose-Refactoring-Kata&quot;&gt;Gilded Rose kata&lt;/a&gt;, which may have colored my
results. It is possible that the AI assistant has been trained on
various solutions to Gilded Rose, and merely memorized the results. I can’t tell if this is true or not, so I can only
report what I know.&lt;/p&gt;

&lt;h1 id=&quot;start-well&quot;&gt;Start Well&lt;/h1&gt;

&lt;p&gt;For my example, I cloned the Python version of the Gilded Rose kata, created a virtual environment and git repository (
via UV), and installed the requirements.&lt;/p&gt;

&lt;p&gt;I fired up my editor, in this case PyCharm.&lt;/p&gt;

&lt;p&gt;Following the usual &lt;a href=&quot;https://www.industriallogic.com/blog/clean-start-protocol/&quot;&gt;Clean Start&lt;/a&gt; protocol, I began by
running all the tests in the project.&lt;/p&gt;

&lt;p&gt;I clicked the “rerun” option so that the full test suite will run automatically when any file changes. I find that this
makes continuous testing a trivial undertaking, and I’m a big believer in both making my work effortless and also
continuously testing as I code.&lt;/p&gt;

&lt;p&gt;Famously, it comes with only one test, which is made to fail. The test ran and failed as expected. Good.&lt;/p&gt;

&lt;p&gt;I used the gnext script, described in &lt;a href=&quot;https://www.industriallogic.com/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt; to set the
intention of my next action.&lt;/p&gt;

&lt;p&gt;I have since put an &lt;a href=&quot;https://github.com/tottinge/intentional-commits&quot;&gt;improved version on github&lt;/a&gt; so you can feel free
to clone it and use these commands on your daily work (without warranty and at your own risk).&lt;/p&gt;

&lt;p&gt;I made the test pass, and used &lt;code class=&quot;highlighter-rouge&quot;&gt;gdone&lt;/code&gt; to commit my changes.&lt;/p&gt;

&lt;h1 id=&quot;add-tests&quot;&gt;Add Tests&lt;/h1&gt;

&lt;p&gt;The whole point of the Gilded Rose is that it is a bit of ugly legacy code without tests.&lt;/p&gt;

&lt;p&gt;The scenario of “I have to work on this code, but there are no tests” is quite familiar to most of us.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gnext &quot;test: add unit tests&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the AI window, I set the scope to be the gilded_rose.py source file, and asked the AI to write unit tests for the
update_quality() method.&lt;/p&gt;

&lt;p&gt;I have used test generators before, and have been uniformly disappointed. They tend to be run-on, ill-structured tests
with meaningless names, but they do provide coverage for code I want to refactor. This is my first time doing this with
an AI.&lt;/p&gt;

&lt;p&gt;In a radical departure from expectations, it did a pretty good job. Not great, but pretty good.&lt;/p&gt;

&lt;p&gt;The tests were of the form:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_normal_item_quality_does_not_go_below_zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Elixir of the Mongoose&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;gilded_rose&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GildedRose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;gilded_rose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
       &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The test naming isn’t too bad. It’s roughly as good as i sometimes see from students in my TDD training.&lt;/p&gt;

&lt;p&gt;The tests follow the Arrange, Act, Assert pattern. They have exactly one action per test, as I prefer. It also is
willing to have multiple assertions when there is more than one evidence of the action, which is also to my taste (
YMMV).&lt;/p&gt;

&lt;p&gt;On the other hand, I don’t like how it subscripts the list to reference the item.&lt;/p&gt;

&lt;p&gt;Some people will argue that the micro-efficiency is poor since it subscripts the array multiple times, and array
indexing isn’t free. I will recognize that, but it is not my objection.&lt;/p&gt;

&lt;p&gt;I worry that the array indexing distracts the reader and complicates the test just a little.&lt;/p&gt;

&lt;p&gt;Since the tests work, I’ve satisfied one level of the Code Virtues and now duplication and simplicity become more
prominent concerns.&lt;/p&gt;

&lt;p&gt;I asked the AI to try again but give the item in each test a name instead of referencing it through the items array. I
decided to push my luck and not give a very concise and clear set of instructions.&lt;/p&gt;

&lt;p&gt;My prompt is imprecise and curt: “&lt;em&gt;rewrite these tests but reference the item directly instead of using items[0]&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;It now created a named object in each test and passed the object in an “immediate” array to the GildedRose constructor.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_aged_brie_increases_quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;aged_brie&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Aged Brie&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gilded_rose&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GildedRose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gilded_rose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used the handy “copy” button from the AI’s code window and pasted it all, replacing the content of the
test_gilded_rose.py file entirely in one easily revertable step.&lt;/p&gt;

&lt;p&gt;The tests ran, and they all passed. So far so good.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gdone
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I suddenly realized that i hadn’t run the tests with coverage, which I think is reasonable if one is going to rely upon
tests for refactoring.&lt;/p&gt;

&lt;p&gt;So, okay, we run with coverage. I have line coverage of the entire source. That’s nice.&lt;/p&gt;

&lt;p&gt;But what about mutation testing? In python, I installed mutmut and configured it quickly. It also found no problems with
the tests. There were no “surviving mutations” from the code.&lt;/p&gt;

&lt;h1 id=&quot;the-production-source&quot;&gt;The “Production” Source&lt;/h1&gt;

&lt;p&gt;At this point, I decided not to follow the standard Gilded Rose script, for fear that the AI assistant was parroting all
the prior answers it had memorized.&lt;/p&gt;

&lt;p&gt;I decided to attack negation in the source.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gnext &quot;refactor: avoid negation in conditions&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My prompt:  &lt;em&gt;“refactor update_quality to avoid using negation in if statements.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a thorny thing to ask, given the nature of Gilded Rose.&lt;/p&gt;

&lt;p&gt;The AI provided some refactored code, and I copied it all and pasted it over the top of the entire gilded_rose.py
contents.&lt;/p&gt;

&lt;p&gt;Immediately the tests failed. The refactoring was not valid.&lt;/p&gt;

&lt;p&gt;That’s okay, I use gnext/gdone to save my state, and I’m
making changes one at a time so that the undo is easy.&lt;/p&gt;

&lt;p&gt;I used undo, and got my original code back: all tests are green.&lt;/p&gt;

&lt;p&gt;My prompt is even less specific: “try again, but be sure all the tests pass”&lt;/p&gt;

&lt;p&gt;I paste, and the tests pass. Surprising! I guess I should have stated that the tests need to pass to begin with. Lesson
learned.&lt;/p&gt;

&lt;p&gt;The code is structured very differently now. Methods have been extracted, and a guard clause added for “Sulfuras”:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sulfuras, Hand of Ragnaros&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# &quot;Sulfuras&quot; never changes in quality or sell_in
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

       &lt;span class=&quot;c1&quot;&gt;# Update `sell_in` for all items except &quot;Sulfuras&quot;
&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

       &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Aged Brie&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
           &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_update_aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Backstage passes to a TAFKAL80ETC concert&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
           &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_update_backstage_passes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
           &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_update_normal_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is more improvement than I’d asked or hoped for. I’m torn whether I like that big change or not, but overall I
think I do.&lt;/p&gt;

&lt;p&gt;All tests pass. At this point, I’ve achieved the intention of this commit, so one more time with &lt;code class=&quot;highlighter-rouge&quot;&gt;gdone&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;cross-class-refactor&quot;&gt;Cross-Class Refactor&lt;/h1&gt;

&lt;p&gt;For fun, I decided to even further break with tradition and modify the Item class.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gnext &quot;refactor: items know their type&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This might not be a thing that I would do normally, but I’m kicking tires. My prompt: “&lt;em&gt;refactor update_quality so that
all the name comparisons are part of the item class.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m intentionally vague, but it seems to understand what I’m asking just the same.&lt;/p&gt;

&lt;p&gt;An excerpt:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;# Helper properties for special item checks
&lt;/span&gt;    
    &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Aged Brie&quot;&lt;/span&gt;
    
    &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_backstage_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Backstage passes to a TAFKAL80ETC concert&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not only did it add name checks to the Item class, it implemented them as properties rather than normal member
functions. I didn’t ask for that, but it’s not a strange or troubling choice.&lt;/p&gt;

&lt;p&gt;The update_quality isn’t odd-looking now, either, so I paste the new code and watch as the tests pass.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GildedRose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_quality&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_sulfuras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Sulfuras has no quality or sell_in changes
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

           &lt;span class=&quot;c1&quot;&gt;# Decrease sell_in for all non-Sulfuras items
&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

           &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
               &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_update_aged_brie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_backstage_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
               &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_update_backstage_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_normal_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
               &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_update_normal_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I can do without the superfluous comments, but that’s a separate intention. With the code valid (passing tests) and the
current intention met, I use gdone again.&lt;/p&gt;

&lt;p&gt;It modified both classes, the caller and the callee, in cooperation. This is some good semantic editing, even if I know
later I will be renaming the functions to be less string-specific and more type-specific. It’s a good step on the path.&lt;/p&gt;

&lt;h1 id=&quot;reducing-conditional-complexity&quot;&gt;Reducing Conditional Complexity&lt;/h1&gt;

&lt;p&gt;The methods for updating various items are riddled with silliness like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gnext &quot;refactor: eliminate needless comparisons to 50&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It seems to me that taking that much space to deal with “increment up to 50 only” is pointless. Can the assistant work
out what I mean from a simple prompt? Let’s try “&lt;em&gt;in _update_backstage_pass, refactor the code to use max instead of
checking the value is less than 50 and then incrementing&lt;/em&gt;.”&lt;/p&gt;

&lt;p&gt;Will it understand?&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_update_backstage_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# After the concert, quality drops to 0
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Increase by 3 if sell_in is 5 or less using `min` to keep it at most 50
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Increase by 2 if sell_in is 10 or less using `min` to keep it at most 50
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Increase by 1 otherwise using `min`
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not only did it understand, it recognized that I should specify &lt;code class=&quot;highlighter-rouge&quot;&gt;min&lt;/code&gt;, rather than &lt;code class=&quot;highlighter-rouge&quot;&gt;max&lt;/code&gt; as I suggested. This is a
signature error - I have done it several times, and then have to go back to it. This time, my AI “programming pair”
didn’t let me do something that backward and silly.&lt;/p&gt;

&lt;p&gt;I paste the code, the tests still all run green. All code has line coverage.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gdone
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;ideating&quot;&gt;Ideating&lt;/h1&gt;

&lt;p&gt;My new partner and I pursued a logical improvement. We hit a few minor snags along the way, but I thought it was
illuminating.&lt;/p&gt;

&lt;p&gt;Here is a bit of ugly conditional code:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_update_backstage_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# After the concert, quality drops to 0
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;change_amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Default increase
&lt;/span&gt;        
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;change_amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Increase by 3 when 5 days or fewer are left
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;change_amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Increase by 2 when 6-10 days are left
&lt;/span&gt;    
       &lt;span class=&quot;c1&quot;&gt;# Update quality and ensure it doesn&apos;t exceed 50
&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;quality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;change_amount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the way of improvement, it suggested this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;change_amount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sell_in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is relatively terse, relatively clear, has pretty formatting, and works.&lt;/p&gt;

&lt;p&gt;On the other hand, I’m not sure that
cascading ternaries will be anyone’s favorite choice for expressing this idea.&lt;/p&gt;

&lt;p&gt;I’m willing to be convinced otherwise.&lt;/p&gt;

&lt;p&gt;It is, however, an implementation I’d not considered before since I have a bit of a prejudice against nested ternary
expressions. The AI tool doesn’t have my prejudices, and may suggest things I wouldn’t have tried. That’s not a bad
thing&lt;/p&gt;

&lt;p&gt;I asked about ways to represent a lookup table instead. It suggested this form:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;lookup_table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;51&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;51&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;c&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookup_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key_range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
           &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;unknown&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# In case value doesn&apos;t fit any range
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This isn’t bad. It is quirky though.&lt;/p&gt;

&lt;p&gt;Using a range object as a dictionary key is unusual. In earlier versions of Python, it wasn’t even legal.&lt;/p&gt;

&lt;p&gt;The weirdness of this even confused my AI Assistant. In some versions of python, the range object isn’t hashable and
can’t be a key. In later versions, it is valid. The AI seemed to be confused about what version we are using, and went
back and forth between recommending this structure and declaring it illegal.&lt;/p&gt;

&lt;p&gt;Since we are not using the dictionary as a dictionary anyway, why use a dictionary?
It seems to be a lot of mechanism for a simple thing. I would think a list of tuples would
be more natural, and we could even have made this a tuple of tuples to keep it immutable,
for what little difference that would make.&lt;/p&gt;

&lt;p&gt;I switched to use the more obvious array of tuples, and didn’t have any issues with either the tests, the compiler, or
the assistant.&lt;/p&gt;

&lt;p&gt;In the next move, it deleted my version and replaced it with the nested if statements again. I can see that I will need
to watch this AI thing very closely if I want to maintain my own updates.&lt;/p&gt;

&lt;h1 id=&quot;impression&quot;&gt;Impression&lt;/h1&gt;

&lt;p&gt;Using an AI can help with proper code craft including coding, testing, and refactoring.&lt;/p&gt;

&lt;p&gt;The fact that it is trained to know programming languages and their libraries means that it may suggest solutions I’d
not considered, though I did find it lacking on knowledge of some python libraries like &lt;em&gt;itertools&lt;/em&gt; and &lt;em&gt;operators&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I’m surprised how my intentionally poor, loose prompts still got good behaviors most of the time.&lt;/p&gt;

&lt;p&gt;I was also surprised at how often I needed to re-center it to use the existing version of the code and not something it
had imagined or suggested earlier in the session.&lt;/p&gt;

&lt;p&gt;I was pleased with how it generated tests for existing code.&lt;/p&gt;

&lt;p&gt;When I’m dealing with legacy code, I will probably extract methods and have an AI provide tests.&lt;/p&gt;

&lt;p&gt;In code other than this kata, I’ve had it generate invalid tests, even some that didn’t compile. When it works, it
works.&lt;/p&gt;

&lt;p&gt;These tools will be helpful to people who work with discipline and proper craft. It may also help people who don’t, but
risks are greater that they will generate troublesome code that they don’t know how to fix.&lt;/p&gt;

&lt;p&gt;With microcommits and tests, I think I’ve limited the damage it can do.&lt;/p&gt;

&lt;p&gt;I will remind you at this juncture that my AI assistant may have been trained on Gilded Rose and may have been merely
regurgitating memorized code samples&lt;/p&gt;

&lt;p&gt;It seems to be doing my bidding with semantic editing, but it could be a convenient parlor trick in this case.&lt;/p&gt;

&lt;p&gt;I will have to try the same techniques and behaviors in code bases the AI has never seen before.&lt;/p&gt;

&lt;p&gt;Don’t turn off your own intelligence, intuition, creativity, and code sense, but consider using AI as a partner. Don’t
trust it too much, but don’t ignore it.&lt;/p&gt;

&lt;p&gt;AI gets better fast; it currently is the worst it will ever be. Where it falls short today it may be soon be more
competent.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See also:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/learn-tdd-from-ai/&quot;&gt;Can You Learn TDD from an AI?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/using-ai-in-your-ide-refactoring/&quot;&gt;AI in your IDE - Refactoring&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/using-ai-in-your-ide-unit-testing/&quot;&gt;AI in your IDE - Unit Testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider&lt;a href=&quot;https://www.industriallogic.com/get-in-touch/&quot;&gt; using Industrial Logic&lt;/a&gt;’s competence to upskill your teams.&lt;/p&gt;
</description>
        <pubDate>Thu, 30 Jan 2025 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/gilded-rose-with-ai-and-pycharm/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/gilded-rose-with-ai-and-pycharm/</guid>
        
        <category>AI</category>
        
        <category>Pycharm</category>
        
        <category>code generation</category>
        
        <category>efficiency</category>
        
        <category>prompt engineering</category>
        
        <category>tech safety</category>
        
        
        <category>Artificial Intelligence</category>
        
        <category>Development</category>
        
        <category>Process Improvement</category>
        
        <category>Refactoring</category>
        
        <category>Tech Safety</category>
        
      </item>
    
      <item>
        <title>Save Your Game</title>
        <description>&lt;h2 id=&quot;series-index&quot;&gt;Series index&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/clean-start-protocol/&quot;&gt;Clean Start Protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/fast-feedback/&quot;&gt;Fast Feedback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/easy-integration/&quot;&gt;Easy Integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/safe-release/&quot;&gt;Safe Releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Welcome to the second stage in the process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-easy-recovery/top-image.png&quot; alt=&quot;a picture of the five-step plan with the words &apos;easy recovery&apos; highlighted&quot; title=&quot;The 2nd in our series&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;the-basic-steps&quot;&gt;The Basic Steps&lt;/h1&gt;

&lt;p&gt;There is a rather lightweight discipline here.
It’s unfamiliar to many people, but it isn’t difficult to learn.
You may find these steps described more succintly
in &lt;a href=&quot;https://intentionalcommits.org/scope-discipline/&quot;&gt;Intentional Commits’ treatment of scope control&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, choose what you will do. This means the next VERY SMALL step, possibly as small as writing a function or even
adding a log message, that moves you a little closer to the goal you have in mind. We’ll discuss this more, below.&lt;/p&gt;

&lt;p&gt;As you work, &lt;em&gt;run all of your checks&lt;/em&gt;. That includes the reformatter (either habitually while editing, or automatically
on save), the lint checkers, the static security scans, and all the tests fast enough to support your minute-by-minute
development work.&lt;/p&gt;

&lt;p&gt;You can &lt;em&gt;commit&lt;/em&gt; any time all the checks are fully successful (they “run green”).&lt;/p&gt;

&lt;p&gt;You can &lt;em&gt;pull&lt;/em&gt; from the main branch after any commit.&lt;/p&gt;

&lt;p&gt;You can &lt;em&gt;push&lt;/em&gt; as long the tests are green and the application works.&lt;/p&gt;

&lt;p&gt;If tests fail, you can either add a fix in the next step or else make a &lt;em&gt;graceful retreat.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;choose-what-you-will-do&quot;&gt;Choose What You Will Do&lt;/h1&gt;

&lt;p&gt;The mindset of taking smaller steps is described as “iterative and incremental development,” “walking skeletons,” “
tracer bullets,” or “evolutionary design.”&lt;/p&gt;

&lt;p&gt;Here, we assume that you have read up on these concepts and are ready to implement your big feature as a series of small
end-to-end changes. It’s too much to try to introduce incremental development as a sidebar to this Easy Recovery topic.&lt;/p&gt;

&lt;p&gt;What step can you take now (ideally within the next hour) that would help solve some part of the problem for some set of
people?&lt;/p&gt;

&lt;p&gt;As a side note: sometimes the thing you most need to do is explore the application and understand the flows you are
fitting your change into.&lt;/p&gt;

&lt;p&gt;You may wish to modify code experimentally to learn how the system will react. Since you began with a Clean Start,
there’s nothing to fear; you can always return to that starting position.&lt;/p&gt;

&lt;p&gt;Once you have made your choice, that is the ONE thing you are going to do.&lt;/p&gt;

&lt;h1 id=&quot;commit-to-doing-just-one-thing-at-a-time&quot;&gt;Commit to Doing Just One Thing (at a time)&lt;/h1&gt;

&lt;p&gt;I’ve become a fan of &lt;a href=&quot;https://intentionalcommits.org/&quot;&gt;Intentional Commits&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a simple idea: choose what you will do next, control your scope, and do the thing.&lt;/p&gt;

&lt;p&gt;If you think of some good side quest, don’t pursue it. Instead, write it down and consider doing it next, after you
finish the current task.&lt;/p&gt;

&lt;p&gt;Intentional commits are a lot like Test-Driven Development.&lt;/p&gt;

&lt;p&gt;In case you need a refresher on TDD:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Choose what behavior you want to create next&lt;/li&gt;
  &lt;li&gt;Write a test (to a hypothetical API) that will expose that behavior&lt;/li&gt;
  &lt;li&gt;Write the code that will pass the test&lt;/li&gt;
  &lt;li&gt;Ensure that the new test and all prior tests are passing&lt;/li&gt;
  &lt;li&gt;Refactor the code toward the design you would like it to ultimately have.&lt;/li&gt;
  &lt;li&gt;Integrate: collect the changes your teammates have pushed and share your changes with the team. When this seems like
overkill, at least “save your game” by committing locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With intentional commits, you:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Choose what you will want to do next, perhaps composing the commit message in advance.&lt;/li&gt;
  &lt;li&gt;Make the change you have planned, and only that change.&lt;/li&gt;
  &lt;li&gt;Anything you realize you also want to do? Write it down. Don’t do it yet.&lt;/li&gt;
  &lt;li&gt;When the change is made and all is “green”, commit with the message you composed earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both cases rely on your ability to slice work small and do just one thing at a time.&lt;/p&gt;

&lt;p&gt;The advantages are convenience, safety, and time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;All the time previously spent composing meaningful commits by selecting passages of code from various files? Gone.
Since you only did one thing, there are no stray changes to separate. Most of the time you can just add
everything (&lt;code class=&quot;highlighter-rouge&quot;&gt;git add .&lt;/code&gt;) and commit.&lt;/li&gt;
  &lt;li&gt;Your commit will be appropriate for a code review (if your company is &lt;em&gt;still&lt;/em&gt; doing asynchronous code reviews).&lt;/li&gt;
  &lt;li&gt;Once you push, you are completely free to do something else. That might be exploration, testing, refactoring, UI
improvements, expanding the functionality you worked on in the last commit, or picking up a side quest you’ve recorded
for yourself.&lt;/li&gt;
  &lt;li&gt;Your work is more visible more often. This means that managers, sponsors, bosses, supervisors and that lot will not
have the same pressure to micromanage you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is safety, convenience, and freedom in working this way.&lt;/p&gt;

&lt;p&gt;The problem, of course, is that &lt;em&gt;95% of preference is familiarity&lt;/em&gt;, and most programmers are only familiar with “load it
all in your head and do all the programming, then look for the problems” from prior practice. Since that’s what they
know, and it’s gotten them this far, that’s what they &lt;em&gt;prefer&lt;/em&gt; to do. This is why people tend to order familiar items
off of a restaurant menu, and go places they’ve already been.&lt;/p&gt;

&lt;p&gt;This isn’t a moral imperative, just an advantageous way of working. There is no moral “should” involved. If you don’t
already do this, that doesn’t make you a bad person. If you like to stick with the old way of working in big steps with
fear of loss, that doesn’t make you a bad person either.&lt;/p&gt;

&lt;p&gt;If you choose to work in small steps, you will get certain advantages. It is an adjustment. You may fall back on habit
sometimes. Maybe you can experiment with this on the side, or try it only on small jobs, or when pairing or working in
an ensemble.&lt;/p&gt;

&lt;p&gt;If you get used to it, you may not want to return to the old way.&lt;/p&gt;

&lt;h1 id=&quot;graceful-retreat&quot;&gt;Graceful Retreat&lt;/h1&gt;

&lt;p&gt;Once I learned to do TDD (probably around 2004?) I got used to the idea that &lt;em&gt;all the tests passed 5-10 minutes ago&lt;/em&gt;.
Since I was working in small steps, I could safely and easily delete my most recent change.&lt;/p&gt;

&lt;p&gt;In the Industrial Logic eLearning, Joshua Kerievsky described it as a “graceful retreat”: you can revert your local
changes using your version control tool.&lt;/p&gt;

&lt;p&gt;In git, that’s &lt;code class=&quot;highlighter-rouge&quot;&gt;git reset –hard&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I know people who would rather amputate a finger than type that sequence of characters. They imagine, perhaps rightly,
that they have too much invested in their current unit of work. The solution sounds overly-simplistic, but it is
actually a simple idea: Don’t invest that much between commits.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2025-easy-recovery/tdd-cycle.png&quot; alt=&quot;a drawing of the four-step TDD process&quot; title=&quot;how it&apos;s done&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For safety’s sake, we strive to never have a large commitment at risk.&lt;/p&gt;

&lt;p&gt;Instead, we do one thing at a time and commit when the tests run green.&lt;/p&gt;

&lt;p&gt;We call these &lt;a href=&quot;https://www.industriallogic.com/blog/whats-this-about-micro-commits/&quot;&gt;microcommits&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Gamers talk about “saving their game”. If you save your game (at least before a boss fight) you can gracefully return
to your healthy pre-battle state.&lt;/p&gt;

&lt;p&gt;It’s the same with software. Thanks to Distributed Version Control Systems (like git), you can save your game at will.&lt;/p&gt;

&lt;p&gt;You certainly should do so before attempting anything tricky or error-prone.&lt;/p&gt;

&lt;p&gt;If you commit locally while the code is in a good state, you can return to that good state with all tests running
successfully and your application still working.&lt;/p&gt;

&lt;p&gt;I was talking with Bryan Beecham, Mike Hill, Ron Jeffries, and Chet Hendrickson and I don’t remember who said what or in
what order, but it was mentioned that we don’t use the reset command often enough. Everyone agreed that it is better to
reset to the last good state and start over rather than fight through a badly started change.&lt;/p&gt;

&lt;p&gt;This is only a sensible thing to do if you are saving your game regularly.&lt;/p&gt;

&lt;h1 id=&quot;calling-your-shot&quot;&gt;Calling Your Shot&lt;/h1&gt;

&lt;p&gt;I’ve gone so far as to create a set of zsh functions to help with my intentional, tiny steps.&lt;/p&gt;

&lt;p&gt;You can find (and try) these via &lt;a href=&quot;https://github.com/tottinge/intentional-commits&quot;&gt;GitHub&lt;/a&gt;. The commands are &lt;code class=&quot;highlighter-rouge&quot;&gt;gnext&lt;/code&gt; to
declare your next change, and then &lt;code class=&quot;highlighter-rouge&quot;&gt;gdone&lt;/code&gt; to commit it or &lt;code class=&quot;highlighter-rouge&quot;&gt;gnope&lt;/code&gt;to delete the message and revert all changes since the
last commit.&lt;/p&gt;

&lt;p&gt;I don’t know if you find this useful or not, but for me it is handy to pre-write my git message, just as it is to write
the test before the code in TDD.&lt;/p&gt;

&lt;p&gt;Even if you don’t use TDD, you could use &lt;code class=&quot;highlighter-rouge&quot;&gt;gnext&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;gdone&lt;/code&gt;to see if it helps you to think in intentional, small steps.
This is provided as an example, no guarantees of any kind included. It works, and it’s the script I use, but take
responsibility for your use of it.&lt;/p&gt;

&lt;h1 id=&quot;but-why&quot;&gt;But WHY?&lt;/h1&gt;

&lt;p&gt;Fear of loss drives poor practice.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When you aren’t afraid to revert, you aren’t afraid to move forward.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test-Driven Development and microcommits are not common (enough) practice, so the whole “easy recovery” thing is a bit
of an open secret.&lt;/p&gt;

&lt;p&gt;Developers often do a large amount of work at once, and their changes are spread out among many lines of code in many
source code files.&lt;/p&gt;

&lt;p&gt;It’s not unusual in some projects to have many hundreds of lines of code in several dozen files, all as part of making a
single change. Sometimes it’s thousands of lines in hundreds of files.&lt;/p&gt;

&lt;p&gt;These are undifferentiated changes in the file system. They’re not segregated and indexed in a way that describes why
the change was made or when. If the developer doesn’t practice the Clean Start protocol, these changes may be mixed with
unrelated changes made previously.&lt;/p&gt;

&lt;p&gt;Eventually, developers will run the code, and maybe even the tests&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;They will find the errors they made hours (or days) ago and will fix the ones they find. They will likely test for side
effects and fix those as well if time and system knowledge are sufficient.&lt;/p&gt;

&lt;p&gt;Then they will commit the current mega-change-set so that they can push it to a shared (or sharable) code branch. This
may be the only time they save their changes other than locally in source files.&lt;/p&gt;

&lt;p&gt;But let’s say it doesn’t go quite that well.&lt;/p&gt;

&lt;p&gt;If they find that they have made a change with puzzling and unwanted consequences, they may have to undo their changes
and redo parts of the work. There is no easy and clear way to do this, so they may spend hours combing over files,
undoing changes, looking for potential side effects or mistyped algorithms, seeking the error, separating the good
changes from the bad ones.&lt;/p&gt;

&lt;p&gt;In the worst-case scenario, it may be so baffling and complex that they are forced to revert the whole change set and
lose all the work they’ve done so far.&lt;/p&gt;

&lt;p&gt;The fear of losing that much work is so significant, they may press on desperately, making change after change.&lt;/p&gt;

&lt;p&gt;We’ve seen people spend days debugging and experimentally “hacking” at the code to avoid losing a prior afternoon’s
work, always certain that they are only one or two more little problems from completion.&lt;/p&gt;

&lt;p&gt;The problem here is that the change is “all or nothing.”&lt;/p&gt;

&lt;p&gt;This kind of practice involves individuals making large changes without any safety mechanisms in place. They don’t think
much about that, though, because they’re used to having a lot of work at risk and usually it turns out okay. It’s “
normal.”&lt;/p&gt;

&lt;p&gt;It shouldn’t be normal. It turns out badly often enough that it causes fear. It encourages people to leave code in a
poor state. It makes the goal of actually implementing a feature such a big accomplishment that people seldom will stay
in the moment and tidy up after themselves. The fact that they managed to get it to work at all is success, and
eliminating the collateral damage feels like a pointless chore to them.&lt;/p&gt;

&lt;p&gt;Mind you, this isn’t the single worst problem in software development, but it is enough of a drag on developers that
people avoid improving the design of the software or trying new ways to achieve new results. They work in a
pathologically conservative way and the code base quality degrades.&lt;/p&gt;

&lt;p&gt;We find it safer to work in small (incremental) steps so we’re not at the ragged edge of human cognition at all times.
We also make it safer by working together, so that we can cover each others’ blind spots.&lt;/p&gt;

&lt;h1 id=&quot;tiny-steps-make-it-all-work&quot;&gt;Tiny Steps Make It All Work&lt;/h1&gt;

&lt;p&gt;You may find it’s faster and easier to start over than to “gut it out” when things are confusing.&lt;/p&gt;

&lt;p&gt;With a lower cost of failure, you may find yourself being more innovative!&lt;/p&gt;

&lt;p&gt;I am confident that you will feel less stress and make more progress when you always know exactly what you are doing and
that you can cheaply and safely step backward.&lt;/p&gt;

&lt;p&gt;It works even better in pairs or ensembles.&lt;/p&gt;

&lt;!-- Footnotes themselves at the bottom. --&gt;

&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;/h2&gt;

&lt;p&gt;Not a joke. We found that a lot of developers never run the test suite at all, but send their changes blindly to
build-and-test server, hoping that the tests will pass there.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;

      &lt;p&gt;&lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 09 Jan 2025 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/save-your-game/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/save-your-game/</guid>
        
        <category>clean start</category>
        
        <category>easy recovery</category>
        
        <category>microcommits</category>
        
        <category>conventional commits</category>
        
        <category>semantic commits</category>
        
        <category>intentional commits</category>
        
        <category>scope control</category>
        
        <category>small steps</category>
        
        <category>graceful retreat</category>
        
        
        <category>Development</category>
        
        <category>Engineering</category>
        
        <category>Software Development</category>
        
        <category>Tech Safety</category>
        
      </item>
    
      <item>
        <title>The Clean Start Protocol</title>
        <description>&lt;h2 id=&quot;series-index&quot;&gt;Series index&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/clean-start-protocol/&quot;&gt;Clean Start Protocol&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/save-your-game/&quot;&gt;Save Your Game&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/fast-feedback/&quot;&gt;Fast Feedback&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.industriallogic.com/blog/easy-integration/&quot;&gt;Easy Integration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/safe-release/&quot;&gt;Safe Releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;strong&gt;clean start&lt;/strong&gt; is the beginning of a great programming session.&lt;/p&gt;

&lt;p&gt;There have been times that I was working on some new code and found an unexpected problem either with the automated
tests or by manually running the code and seeing a flaw that wasn’t covered by a test.&lt;/p&gt;

&lt;p&gt;I have spent hours trying to understand how my code caused such a failure, only to learn the fault wasn’t mine. It was
usually that someone else had pushed a bad commit to the shared branch, and I pulled without noticing it.&lt;/p&gt;

&lt;p&gt;Though it pains me to admit it, I’ve also been the one who pushed code that couldn’t work in CI or on my coworkers’
machines. It was usually that I had something on my machine that wasn’t on everyone else’s machine; a library, module,
data file, or compiled library that was not a part of the configuration.&lt;/p&gt;

&lt;p&gt;I’ve had the same experience when pairing or mobbing when my coworker did not start clean.&lt;/p&gt;

&lt;p&gt;Controlling the dependencies and quality of the code is crucial for CI and CD and is a good all-around discipline.&lt;/p&gt;

&lt;p&gt;It is surprising how many people have no “clean start” discipline despite having been through degree programs and years
of work experience. I certainly didn’t for decades of my early career (no shame in it, just acknowledgment).&lt;/p&gt;

&lt;p&gt;The Modern Agile wheel includes an element in the bottom, shown in blue, that states “Make Safety the Prerequisite.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/modern-agile-wheel.png&quot; alt=&quot;the modern agile wheel, showing the text snippets for &amp;quot;make people awesome&amp;quot;, &amp;quot;make safety the prerequisite&amp;quot;, &amp;quot;experiment and learn rapidly&amp;quot;, and &amp;quot;deliver value continuously&amp;quot;&quot; title=&quot;image_tooltip&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This means that we should begin in such a way that we will not be unnecessarily at risk of loss or unnecessary struggle.&lt;/p&gt;

&lt;p&gt;One part of the safety prerequisite is to have tests, particularly fast-running automated checks.&lt;/p&gt;

&lt;p&gt;We need a way of quickly validating that our system is working. If we have to manually test all the features of complex
systems, we might have no time left over for programming!&lt;/p&gt;

&lt;p&gt;If you don’t have fast automated tests, it’s a good time to start building some, but keep reading so you understand what
advantages they will give you at the start of each new unit of work.&lt;/p&gt;

&lt;p&gt;We will take automated checks as a given.&lt;/p&gt;

&lt;h1 id=&quot;what-is-a-clean-start&quot;&gt;What is a Clean Start?&lt;/h1&gt;

&lt;p&gt;I begin new work ensuring the workspace is free from pollution, leftovers, failing tests, and half-finished work.&lt;/p&gt;

&lt;p&gt;We will put our workspace into a “clean” state before beginning each job, even if the job is working in the same code
base and the same branch (we only use ‘main’).&lt;/p&gt;

&lt;p&gt;We don’t switch jobs without ensuring a clean start.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2024-clean-start/clean-start-main.png&quot; alt=&quot;an image showing five steps for programming, and a highlight on the first that reads &amp;quot;clean start&amp;quot;&quot; title=&quot;You may be surprised that people do this. Or that they don&apos;t.&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;step-1-git-status&quot;&gt;Step 1: Git Status&lt;/h1&gt;

&lt;p&gt;Make sure that you have no untracked files in your workspace. None.&lt;/p&gt;

&lt;p&gt;If you have any, use the appropriate step:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If they are created programmatically, have them deleted programmaticaly.&lt;/li&gt;
  &lt;li&gt;Add their directory to .gitignore if (and only if) the file is harmless and generated by tools during linting, editing, or checking.&lt;/li&gt;
  &lt;li&gt;Remove them by hand (scratch dev files, temp data files, etc).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you are done, you should be greeted with this happy message:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blog/2024-clean-start/git-status-clean.png&quot; alt=&quot;alt_text&quot; title=&quot;how it should look&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You want to make sure you have no uncommitted changes to any of the files in your codebase. You can commit them or
revert them, but you cannot have any uncommitted changes.&lt;/p&gt;

&lt;p&gt;You need to be free of the work you’ve been doing before you start a new job.&lt;/p&gt;

&lt;p&gt;Achieve this first.&lt;/p&gt;

&lt;h1 id=&quot;step-2-git-pull-r&quot;&gt;Step 2: &lt;code class=&quot;highlighter-rouge&quot;&gt;git pull –r&lt;/code&gt;&lt;/h1&gt;

&lt;p&gt;Pull the current working version to your machine.&lt;/p&gt;

&lt;p&gt;Since you’re entirely clean locally, there is no reason to rebase, but I find &lt;code class=&quot;highlighter-rouge&quot;&gt;-r&lt;/code&gt; to be a good habit, and I don’t want
to have to decide whether to do it or not.&lt;/p&gt;

&lt;p&gt;You want the most current and most integrated version available to you. You shouldn’t have to decide when to do this; it
should be automatic.&lt;/p&gt;

&lt;p&gt;You might be surprised how many people forget this, and base their new work on some old version of the main branch.
Don’t make that mistake!&lt;/p&gt;

&lt;p&gt;Why the &lt;code class=&quot;highlighter-rouge&quot;&gt;-r&lt;/code&gt;? Well, I use &lt;code class=&quot;highlighter-rouge&quot;&gt;pull -r&lt;/code&gt; whenever I pull and find it a useful practice, so I want the habit to be to use the
rebase flag all the time. I trust habits more than decisions in this case. It’s harmless enough in this case, and I can
always just recall the last pull command rather than retype it.&lt;/p&gt;

&lt;p&gt;So now you have nothing unique in your space, and you are current with the main branch as far as the file content is
concerned. You’re still not ready to start work.&lt;/p&gt;

&lt;h1 id=&quot;step-3-update-the-local-environment&quot;&gt;Step 3: Update the Local Environment&lt;/h1&gt;

&lt;p&gt;This step depends on your application stack.&lt;/p&gt;

&lt;p&gt;If you are in Python, then you will use your local build/dep tool to update. This is often pip, poetry, or pants.&lt;/p&gt;

&lt;p&gt;If you’re in Javascript or typescript it might be npm.&lt;/p&gt;

&lt;p&gt;In Java, it is likely Gradle, Maven, Ivy, or Ant.&lt;/p&gt;

&lt;p&gt;Whatever the tooling, make the environment current. It is too easy to overlook this step and end up with tests failing
or even failing to run.&lt;/p&gt;

&lt;p&gt;Now you have a fresh environment, and there are only a few steps left.&lt;/p&gt;

&lt;h1 id=&quot;step-4-clean-build&quot;&gt;Step 4: (Clean) Build&lt;/h1&gt;

&lt;p&gt;Compiled languages often require a build. A &lt;em&gt;clean&lt;/em&gt; build deletes any artifacts left over from prior builds, and that’s
the
preferable situation.&lt;/p&gt;

&lt;p&gt;Since you’ve done nothing yet, are current, and have a clean environment, there is no good reason to have trouble
building the current application.&lt;/p&gt;

&lt;p&gt;If the build fails, there is a problem with your local environment to solve before you touch a line of code.&lt;/p&gt;

&lt;p&gt;Clear up any problems that emerge at this point, while you know it’s not your fault.&lt;/p&gt;

&lt;h1 id=&quot;step-5-run-the-tests&quot;&gt;Step 5: Run The Tests&lt;/h1&gt;

&lt;p&gt;Run your test suite(s). There should be no failing tests at all.&lt;/p&gt;

&lt;p&gt;Are the tests running in the CI pipeline? You should double-check that. Maybe you need to correct your machine’s global
environment!&lt;/p&gt;

&lt;p&gt;If tests are failing on your machine and the CI environment, you can be sure you’ve inherited that problem from the main
branch. It’s not your fault, and you may not have to be the one to solve it.&lt;/p&gt;

&lt;p&gt;If you investigate the failure and solve it yourself (after informing the person who pushed the change), you do so
knowing that you don’t have ANY local changes that might have contributed to the failure.&lt;/p&gt;

&lt;p&gt;I like this “clean room” approach.&lt;/p&gt;

&lt;h1 id=&quot;develop&quot;&gt;DEVELOP!&lt;/h1&gt;

&lt;p&gt;Now if anything fails to build or test successfully, it’s because of the code you have just written, and you can solve
that at your workstation. You won’t spend hours or days chasing other people’s problems.&lt;/p&gt;

&lt;p&gt;As you develop, you will probably want to run the tests frequently (maybe automatically, in the background?) so you find
out about problems while the “search space” for the failure is quite small.&lt;/p&gt;

&lt;p&gt;You will likely want to do &lt;a href=&quot;https://www.industriallogic.com/blog/whats-this-about-micro-commits&quot;&gt;microcommits&lt;/a&gt; as you
work, or at least right before starting anything tricky or interesting.
We will discuss this more in forthcoming articles.&lt;/p&gt;

&lt;p&gt;Don’t leave it up to your &lt;a href=&quot;https://www.industriallogic.com/blog/history-microtests/&quot;&gt;microtests&lt;/a&gt;, though.&lt;/p&gt;

&lt;p&gt;Consider having all the human augmentation available and affordable in
your editor. I like using SonarLint (because it’s free), and any other checkers I can find. I’m trying some AI tools,
though the results so far are mixed.&lt;/p&gt;

&lt;p&gt;In Python, I use &lt;em&gt;type hints&lt;/em&gt; and &lt;em&gt;protocols&lt;/em&gt; to help my IDE and various code-checkers spot problems as I’m editing the
code. It saves me time to keep warnings out of my code.&lt;/p&gt;

&lt;p&gt;I use automatic reformatting rather than making a mess for now, only to tidy it up with an automated reformatter later..&lt;/p&gt;

&lt;p&gt;BTW: any time you have committed, you can also do the above 5 clean start steps again to maintain currency with the
development version and avoid a big merge conflict later.&lt;/p&gt;

&lt;h1 id=&quot;committing&quot;&gt;Committing&lt;/h1&gt;

&lt;p&gt;Since you have no changes except those related to &lt;em&gt;the one thing&lt;/em&gt; you are doing, you don’t have to spend time composing
a commit out of selected passages from selected files.&lt;/p&gt;

&lt;p&gt;You will still have to check (&lt;code class=&quot;highlighter-rouge&quot;&gt;git status&lt;/code&gt; again) to be sure you’ve added all the files to version control, or at least
do a global add (&lt;code class=&quot;highlighter-rouge&quot;&gt;git add .&lt;/code&gt; at the root) to get all the files included. Just be sure you don’t commit temporary files
that need to be deleted or moved instead.&lt;/p&gt;

&lt;p&gt;You may be interested in the discipline of &lt;a href=&quot;https://intentionalcommits.org/&quot;&gt;Intentional Commits.&lt;/a&gt; I strongly recommend
an intentional approach to work, so you are doing one thing at a time.&lt;/p&gt;

&lt;p&gt;When you are focused on doing one thing at a time, you won’t get confused or befuddled, nor will you lose your thread if
you are interrupted (as often happens when one is in a side-quest of a side-quest of a side-quest).&lt;/p&gt;

&lt;p&gt;You might find it helpful to add precommit hooks to do a new build and run tests again.&lt;/p&gt;

&lt;p&gt;I’ve gone so far as to write zsh functions so that I can compose my commit message before doing the work and then
commit (with ‘-a’) afterward, using the message I’ve composed.&lt;/p&gt;

&lt;p&gt;If that sounds like a lot of discipline to you, I will comfort you with the knowledge that I never have to carefully
compose all my commits. It’s always all-or-nothing, always easy, and I am not burdened with excess decision-making. I
have found it easier and less stressful this way.&lt;/p&gt;

&lt;h1 id=&quot;make-clean-start-easy&quot;&gt;Make Clean Start Easy&lt;/h1&gt;

&lt;p&gt;There are 5 steps in the “clean start” protocol described above. That sounds like a lot, and even I don’t always remember to
work through the whole list.&lt;/p&gt;

&lt;p&gt;I find it’s easier to automate such a simple process than to memorize it.&lt;/p&gt;

&lt;p&gt;I like to create a script (I usually call it &lt;code class=&quot;highlighter-rouge&quot;&gt;prepare&lt;/code&gt;, though maybe I should call it &lt;code class=&quot;highlighter-rouge&quot;&gt;cleanstart&lt;/code&gt;?) that will do a
git status, a pull, update the environment,
build, and run tests.&lt;/p&gt;

&lt;p&gt;It’s trivial for me to type &lt;code class=&quot;highlighter-rouge&quot;&gt;./prepare&lt;/code&gt; before beginning any
new work, and trivial to remember to so do.&lt;/p&gt;

&lt;p&gt;It is easy to write a bash/zsh script that exits if any of the steps fail. I can’t imagine any reason not to do so. I
usually have a &lt;code class=&quot;highlighter-rouge&quot;&gt;./prepare&lt;/code&gt; script and a &lt;code class=&quot;highlighter-rouge&quot;&gt;./run&lt;/code&gt; script in the root directory of a project just to keep it simple and
easy.&lt;/p&gt;

&lt;p&gt;Here is one actual example from a python program I work on regularly:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c&quot;&gt;#! /bin/bash -e&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;venv/bin/activate
    git pull &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt;
    pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--upgrade&lt;/span&gt; pip
    pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; requirements.txt
    pytest
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Completed successfully.&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;-e&lt;/code&gt; tells &lt;code class=&quot;highlighter-rouge&quot;&gt;bash&lt;/code&gt; to exit on the first failed command, so the rest of the script isn’t performed after a failure.&lt;/p&gt;

&lt;p&gt;If I run it from the wrong directory, the &lt;code class=&quot;highlighter-rouge&quot;&gt;source&lt;/code&gt; command fails, and if I’m in the correct directory then it’s
harmless.&lt;/p&gt;

&lt;p&gt;If I have uncommitted changes, it fails with this message:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    error: cannot pull with rebase: You have unstaged changes.
    error: Please commit or stash them.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I upgrade pip before using it to install new requirements. A lot of my newer projects use poetry, and I’m looking at
perhaps moving forward from there soon, but this project has been using pip all along and I’ve not felt the urge to
modernize it yet.&lt;/p&gt;

&lt;p&gt;Pytest runs all the tests, and stops the script execution if any active (un-skipped) tests does not pass.&lt;/p&gt;

&lt;p&gt;The script takes on the order of 5 seconds when pip is up to date, there is nothing new on the main codeline, and there
are no new requirements. That’s a very small amount of overhead to ensure that I’m truly ready to start my work.&lt;/p&gt;

&lt;p&gt;If you begin with the Clean Start Protocol, I’m sure you will find your work becomes more tidy and effective. It also
provides a base situation for the next 4 steps of this process.&lt;/p&gt;

&lt;p&gt;What did I leave out? Maybe you can add steps I’ve forgotten to mention?&lt;/p&gt;
</description>
        <pubDate>Fri, 13 Dec 2024 15:00:00 +0000</pubDate>
        <link>https://www.industriallogic.com/blog/clean-start-protocol/</link>
        <guid isPermaLink="true">https://www.industriallogic.com/blog/clean-start-protocol/</guid>
        
        <category>starting clean</category>
        
        <category>programming</category>
        
        <category>prepare</category>
        
        <category>start well</category>
        
        <category>small steps</category>
        
        <category>microcommits</category>
        
        <category>conventional commits</category>
        
        <category>semantic commits</category>
        
        <category>bash script</category>
        
        
        <category>Agile</category>
        
        <category>Development</category>
        
        <category>Engineering</category>
        
        <category>Process Improvement</category>
        
        <category>Software Testing</category>
        
        <category>Tech Safety</category>
        
      </item>
    
  </channel>
</rss>
