<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.marcusoft.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.marcusoft.net/" rel="alternate" type="text/html" /><updated>2026-03-03T14:22:41+00:00</updated><id>https://www.marcusoft.net/feed.xml</id><title type="html">marcusoft.net - sharing is learning</title><subtitle>Learning by sharing since 2006</subtitle><entry><title type="html">Exporting speaker notes from presentations</title><link href="https://www.marcusoft.net/2026/03/exporting-speaker-notes.html" rel="alternate" type="text/html" title="Exporting speaker notes from presentations" /><published>2026-03-02T04:00:00+00:00</published><updated>2026-03-02T04:00:00+00:00</updated><id>https://www.marcusoft.net/2026/03/exporting-speaker-notes</id><content type="html" xml:base="https://www.marcusoft.net/2026/03/exporting-speaker-notes.html"><![CDATA[<p>Now that we are using agents to help us with a lot of daily tasks I realize how much information I have put in speaker notes. I have books worth of information in there, due to my bad memory. I knew it would pay off, someday.</p>

<p>And they are really tricky to share with in an AI chat, like (we’re not using <a href="https://quitgpt.org/">Chat GPT anymore, since this morning</a> - right?) … well you choose. I’m going with Claude.</p>

<p>Uploading some of my presentations to an agent means 50+ Mb file, when I’m really just interested in the text in my speaker notes.</p>

<p>Over the years I have been going back and forth between different presenting softwares and hence I have a bit of a problem getting hold of the speaker notes.</p>

<p>But I’ve solved this now. Let me share it with you. And then let me share how I have fixed all of this with a Claude Coworker skill.</p>

<!-- excerpt-end -->

<h2 id="pptx---the-common-denominator">PPTX - the common denominator</h2>

<p>Turns out that many of these formats that I have used over the years does not have a good format to export speaker notes. The only one that does export speaker notes in a good way is Power Point. Luckily it’s also the oldest and has a lot of automation capabilities.</p>

<p>Ok - so I need to convert my stuff to Power Point format. That is a very common way of doing it.</p>

<h2 id="export-speaker-notes-using-python">Export speaker notes using Python</h2>

<p>Python has a tool called <code class="language-plaintext highlighter-rouge">python-pptx</code> (install with <code class="language-plaintext highlighter-rouge">pip install python-pptx</code>) that can read PPTX files. And with that in hand we can write this function and get it to print our speaker notes to the console.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Requires: pip install python-pptx
</span><span class="kn">import</span> <span class="nn">pptx</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="k">def</span> <span class="nf">get_notes</span><span class="p">(</span><span class="n">ppt_path</span><span class="p">):</span>
    <span class="n">prs</span> <span class="o">=</span> <span class="n">pptx</span><span class="p">.</span><span class="n">Presentation</span><span class="p">(</span><span class="n">ppt_path</span><span class="p">)</span>
    <span class="n">notes</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">slide</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">prs</span><span class="p">.</span><span class="n">slides</span><span class="p">):</span>
        <span class="n">note</span> <span class="o">=</span> <span class="n">slide</span><span class="p">.</span><span class="n">notes_slide</span>
        <span class="k">if</span> <span class="n">note</span> <span class="ow">and</span> <span class="n">note</span><span class="p">.</span><span class="n">notes_text_frame</span><span class="p">.</span><span class="n">text</span><span class="p">:</span>
            <span class="n">notes</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s">"Slide </span><span class="si">{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">:</span><span class="se">\n</span><span class="si">{</span><span class="n">note</span><span class="p">.</span><span class="n">notes_text_frame</span><span class="p">.</span><span class="n">text</span><span class="si">}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">notes</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">get_notes</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span>
</code></pre></div></div>

<p>Very cool - we are getting there.</p>

<h2 id="docker">Docker</h2>

<p>But I really hate running Python on my machine. I have never got to terms with all the <code class="language-plaintext highlighter-rouge">venv</code>, <code class="language-plaintext highlighter-rouge">pip</code>s and versions of Python.</p>

<p>Let’s use a Docker file to back all of this into something where Python works</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> python:3.11-slim</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">RUN </span>pip <span class="nb">install</span> <span class="nt">--no-cache-dir</span> python-pptx
<span class="k">COPY</span><span class="s"> extract_notes.py .</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["python", "extract_notes.py"]</span>
</code></pre></div></div>

<p>Now I can run the script without polluting my computer, with a lot of Python stuff that I never use for anything else. Nice!</p>

<h2 id="script">Script</h2>

<p>To run this I need to write these commands</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> pptx-notes <span class="nb">.</span>

docker run <span class="nt">--rm</span> <span class="nt">-v</span> <span class="s2">"</span><span class="nv">$DIR</span><span class="s2">"</span>:/data pptx-notes <span class="s2">"/data/</span><span class="nv">$BASE</span><span class="s2">"</span>
</code></pre></div></div>

<p>Cool, but I will never remember that. Let’s create a script, `run.sh´.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Usage: ./run.sh yourfile.pptx"</span>
  <span class="nb">exit </span>1
<span class="k">fi

</span><span class="nv">INPUT_FILE</span><span class="o">=</span><span class="si">$(</span><span class="nb">realpath</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="si">)</span>
<span class="nv">DIR</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$INPUT_FILE</span><span class="s2">"</span><span class="si">)</span>
<span class="nv">BASE</span><span class="o">=</span><span class="si">$(</span><span class="nb">basename</span> <span class="s2">"</span><span class="nv">$INPUT_FILE</span><span class="s2">"</span><span class="si">)</span>

docker build <span class="nt">-t</span> pptx-notes <span class="nb">.</span>

docker run <span class="nt">--rm</span> <span class="se">\</span>
  <span class="nt">-v</span> <span class="s2">"</span><span class="nv">$DIR</span><span class="s2">"</span>:/data <span class="se">\</span>
  pptx-notes <span class="se">\</span>
  <span class="s2">"/data/</span><span class="nv">$BASE</span><span class="s2">"</span>
</code></pre></div></div>

<p>Very cool - now I can run it like this, to output the result into a text document that I can share with an AI.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bash ./run.sh ~/Downloads/Agile<span class="se">\ </span><span class="k">for</span><span class="se">\ </span>real.pptx <span class="o">&gt;</span> agile_for_real_speaker_notes.txt
</code></pre></div></div>

<h2 id="but-ai">But, AI?</h2>

<p>Yes, obviously I wrote this tool with the help of AI.</p>

<p>But I could also have asked Claude CoWork to do it for me. In fact I did. But then I looked that the skills it used and I wanted to understand deeper how that would work, so tried to do it for myself. I might do the next conversion with Claude CoWork, I might use this. Now I understand the concept and approach, and have built a tool.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="AI" /><category term="Life of a consultant" /><summary type="html"><![CDATA[Now that we are using agents to help us with a lot of daily tasks I realize how much information I have put in speaker notes. I have books worth of information in there, due to my bad memory. I knew it would pay off, someday. And they are really tricky to share with in an AI chat, like (we’re not using Chat GPT anymore, since this morning - right?) … well you choose. I’m going with Claude. Uploading some of my presentations to an agent means 50+ Mb file, when I’m really just interested in the text in my speaker notes. Over the years I have been going back and forth between different presenting softwares and hence I have a bit of a problem getting hold of the speaker notes. But I’ve solved this now. Let me share it with you. And then let me share how I have fixed all of this with a Claude Coworker skill.]]></summary></entry><entry><title type="html">Navigating uncertainty - staying positive when AI is disrupting my job</title><link href="https://www.marcusoft.net/2026/03/navigating-uncertainty-ai-edition.html" rel="alternate" type="text/html" title="Navigating uncertainty - staying positive when AI is disrupting my job" /><published>2026-03-02T04:00:00+00:00</published><updated>2026-03-02T04:00:00+00:00</updated><id>https://www.marcusoft.net/2026/03/navigating-uncertainty-ai-edition</id><content type="html" xml:base="https://www.marcusoft.net/2026/03/navigating-uncertainty-ai-edition.html"><![CDATA[<p>Paradigm shift. Pretty cool to be part of another, huh? But also; scary, lots of big unknowns and the rate of change is like nothing I’ve ever experienced before.</p>

<p>Yes - I’m talking about AI. But this post will be about some ideas how to navigate this change, trying to calm ourselves a bit and, quite frankly, give some comfort. Hopefully.</p>

<p>Someone asked me a question just as I left the office this Friday. The question was really good and the person showed genuine worry. I’m sure that person was not the only one. At least one more share the feelings. Me.</p>

<p>I wanted to share my response, a bit embroidered, here. The question was basically this, but better formulated:</p>

<blockquote>
  <p><em>Marcus, how do you stay positive in the light of all potential downsides of AI?</em></p>
</blockquote>

<!-- excerpt-end -->

<p>The background was that I had just shared some stories from speaking at <a href="https://techarena.se/">Tech Arena</a> — an event packed with AI stuff. It was a really positive vibe in there, but dear God so many new things, and big changes. My head was spinning.</p>

<p>I shared that and in doing so probably contributed to the feeling of overwhelming and scare (What? You haven’t tried <a href="https://paddo.dev/blog/ralph-wiggum-autonomous-loops/">Ralph Loops</a>, it’s several weeks old?!)</p>

<p>I don’t know if I am that positive really, but I’m old. I’ve been 3-4 of these paradigm shifts (the .com boom, the cloud, agile, the tab/spaces wars - I’ve been around). Even if I’ve never had a clear plan going into any of these paradigm shifts, I’ve seen things that works better than others, when it comes to navigating uncertainty.</p>

<h2 id="being-in-a-paradigm-shift-is-scary">Being IN a paradigm shift is scary**</h2>

<p>Acknowledge that. We don’t know what the future holds. Let me say that again; we DO NOT know how this will play out. No one. On the planet. That is scary. It’s ok to be scare, worried, annoyed and confused. And it’s ok to be kind to people around you that feels like that around you.</p>

<p><em>I’m worried, confused and scared too</em>. I kinda like refactoring code, will that go away? Is Agile even a thing in the AI-infused future? Or Consulting?</p>

<p>It’s scary stuff to think about.</p>

<p>Feeling bad yet? :) Ok - here’ comes the comfort-part.</p>

<p>However, in none of these shifts that I have been through - it has never been as bad as the worst I thought. And never as good as the best I thought. Some ideas that I thought was awesome didn’t stick. Some tools that sucked did stick (looking at you JIRA). You will learn things that will not become anything good (huh, tabs are better… who knew) - but you have learned in the process. The learning was the thing. Speaking of.</p>

<h2 id="our-profession-is-learning">Our profession is learning</h2>

<p>Both as product developers (in the wider sense of the word), and consultants. The tools, ways, ideas and needs are ever changing. We have chosen to be in school for ever. Welcome!</p>

<p>We need to keep learning in the paradigm shift too. But when you do - learn deeper, reflect and think deeply. Use the tool, but focus on the practice. Do the practice but try to understand the principle behind it.</p>

<p>For example; so Ralph loop is a new thing that might be a big thing. Or not. Learn about Ralph loops (the tool), use it in Claude Code (the tool), but when you do - think about HOW you are using it (the practice). Then, after you used it for awhile - think about WHY they invented the Ralph Loop (the principle); what problems did they want to solve, why was that a problem, which ideas led them to come up with Ralph Loop as an idea.</p>

<p>By lifting yourself to the principle-level of any practice and idea you will gain a super power in understanding the next tool or practice easier, and faster. Most of the ideas that pops up builds on previous ideas and concepts - so by understanding the concepts you are future proofing yourself.</p>

<p>But you will have to <strong>Keep learning.</strong> This can be done by reading (listening or watching) something, but I’ve found that reflecting on you practice will help you know what to learn.</p>

<p>Also; the best way to learn is to share. Write a blog, as if someone was reading (I did for the first 8 years of my blog existence), or use an AI as a learning partner, or better yet make a presentation and share it. Teaching is the best way to learn deeper.</p>

<p>Let me start by sharing - here’s a great talk that I’ve used to think <a href="https://www.infoq.com/presentations/Embracing-Uncertainty">differently about uncertainty.</a></p>

<h2 id="jevons-paradox">Jevons paradox</h2>

<p>Let’s get some comfort history and people even older than I.</p>

<p>Have you met <a href="https://en.wikipedia.org/wiki/Jevons_paradox">William Stanley Jevons</a>? He’s an economist that, in 1865, was going through a paradigm shift like we are now. Someone invented the steam engine and it was considered, rightly, a big threat to manual labor. It promised to replace ALL WORKERS EVERYWHERE, no one will ever have to work in the same way again, everyone will be unemployed (&lt;= this is me being liberal with history, but you see the similarities, right?)</p>

<p>In 1865, Jevons noticed that when the steam engine got way more <em>efficient</em> at using coal, total coal consumption didn’t go down — it <em>skyrocketed</em>. Because coal was now cheaper per unit of useful work, people found way more things to use it for. The paradox is that making a resource more efficient to use can actually increase overall demand for it, because the drop in cost opens up a flood of new applications.</p>

<p>AI makes “cognitive work” dramatically more efficient. So the fear is that we need <em>fewer humans</em>. But if Jevons holds, the opposite happens — because it’s now cheaper and faster to build software, analyze data, write content, explore ideas… the demand for those things explodes. More products get built. More experiments get run. More ideas get explored. The total amount of “cognitive work” goes up, not down.</p>

<p>In short, history (and Mr Jevons) suggests that when you make something dramatically cheaper, you don’t get less of it — you get wildly more of it, in ways no one predicted.</p>

<h2 id="stay-positive">Stay positive</h2>

<p>Staying positive has value in itself. If you go in trying to find the good, joy and positive vibes you will see other things than if you going in hating the idea. This is true to a point; you need to combine the positiveness with a healthy skepticism. But I think positive with skepticism, will get you to a better place than negative with skepticism.</p>

<p>Our thoughts can actually shape our future, like that. Be a positive change. Focus on the good. You don’t need to not learn the bad - that will take care of itself. When you see someone around you struggling, pull them up. Maybe you have another spin on it that can help them. Tomorrow they will help you.</p>

<p>Being through something hard is easier when you are more people. Looking for better will make it easier than looking for worse.</p>

<h2 id="outro">Outro</h2>

<p>You are probably already ahead, just by feeling a bit worried and confused. You have already started to reflect. Good on you - this is the start of improving.</p>

<p>Let me share what I shared with each class at School of Applied Technology, where I was head of curriculum (headmaster for normal people) for 5 years. On their first and their last day I repeated these approaches to work, learning and people around you:</p>

<ul>
  <li><strong>Be curious</strong> - our job is to learn. Keep learning. It’s fun - imagine that we live in a time where we can learn new things happening every day. I much rather be there than in a world where it’s all the same.</li>
  <li><strong>Be humble</strong> - you don’t know everything. There might be better ways. Look for them. Understand them. Take help. Also - you probably know more than others in some areas. Help out, learn by sharing.</li>
  <li><strong>Be kind</strong> - others does not know what you know. Share, help and support. Treat their confusion, worry and sadness with empathy and kindness. That is good for them. And soon you might need it right back.</li>
</ul>

<p>My friend, asking the initial question; we’ll get through this one too. I’m actually positive right now, that it will be something great in the end. Looking forward to learning more with you and others.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="AI" /><category term="Life of a consultant" /><summary type="html"><![CDATA[Paradigm shift. Pretty cool to be part of another, huh? But also; scary, lots of big unknowns and the rate of change is like nothing I’ve ever experienced before. Yes - I’m talking about AI. But this post will be about some ideas how to navigate this change, trying to calm ourselves a bit and, quite frankly, give some comfort. Hopefully. Someone asked me a question just as I left the office this Friday. The question was really good and the person showed genuine worry. I’m sure that person was not the only one. At least one more share the feelings. Me. I wanted to share my response, a bit embroidered, here. The question was basically this, but better formulated: Marcus, how do you stay positive in the light of all potential downsides of AI?]]></summary></entry><entry><title type="html">How did I learn to code</title><link href="https://www.marcusoft.net/2026/02/how-did-i-learn-to-code.html" rel="alternate" type="text/html" title="How did I learn to code" /><published>2026-02-06T04:00:00+00:00</published><updated>2026-02-06T04:00:00+00:00</updated><id>https://www.marcusoft.net/2026/02/how-did-i-learn-to-code</id><content type="html" xml:base="https://www.marcusoft.net/2026/02/how-did-i-learn-to-code.html"><![CDATA[<p>I asked a few people in the Umain office that are relatively new to programming how they learn in the age of AI. And they graciously shared. And I learned A LOT from that.</p>

<p>It’s a VERY different world now, picking up programming when AI has “always” been around. You have to actively disregard help from the tools, to learn. And actively go into learning mode to actually learn.</p>

<p>Since they shared with me it’s not more than right that I do the same. Sorry, this will be long - I’m old.</p>

<!-- excerpt-end -->

<p>When it comes to programming itself I started way back on a Vic20, learning basic. But I never really got any skills that way. We typed programs from long listings in magazines. I did eventually apply for University (1994) and even though I didn’t think it would be for me and it was very close that I couldn’t get in, I got in and out 4 years later.</p>

<p>I was very lucky to have study-buddy that was a great programmer (Thank you, Mats) so much of my early experience there was basically just sitting next to him and mimicking a lot of his approach. In Uni I also got a lot of foundational stuff down that I didn’t see the use of then, but has later.
At this point I really didn’t enjoy coding, but I could do it. More follow along than being creative.</p>

<h2 id="first-professional-experiences">First professional experiences</h2>

<p>My first job in this industry was an onboarding program / job at a big insurance company, where I directly got introduced to enterprise ways. It was quite confusing I can tell you, but mostly on the “where on earth do I fit in in the puzzle”-level. I was doing C++ coding then, and we were mostly following a structure that was in place. Shoveling data between different databases and making some structural changes between in- and output. It never really felt … real to me. More abstract and weird - coding from a detailed (pseudo-code) spec. I did learn a lot of about managing projects by <a href="https://www.marcusoft.net/2020/08/role-models-staffan-the-consultant.html">Staffan - such a cool dude</a>!</p>

<p>I then (2000-ish) started to work with CapGemini (because I felt that my opportunities were minimized at the insurance company). Here I had the joy of coming into a GREAT team of developers and was swept up in the culture, style and approach to development. A few of these people are still in my wider network today. I especially learnt a lot from a guy that had worked a few years more than me, just moving in from Norway - Öystein. We worked side by side for a few years and wrote a few projects from scratch.</p>

<h2 id="falling-in-love">Falling in love</h2>

<p>HERE is when I fell in love with programming. When I started to be able to put the pieces together by myself. I did that by reading articles, <a href="https://www.marcusoft.net/2018/03/my-top-books.html">books on programming</a> and watching videos. But moreover I had the opportunity to learn from people around me, some were very good at sharing, lifting others up (me to responsibilities I didn’t deserve) and sharing. There’s a mantra from that time that I still think about and use; Ask To Know To Learn To Share.</p>

<p>I got assigned a project that grew to be big and I was suddenly in charge of the architecture and a team. I read more, <a href="https://www.marcusoft.net/2018/03/my-top-books.html">books on programming</a> mostly, but blogs too. The application went on to be a 5 year project that eventually was sold to a company where we were asked to make it into a configurable product. I learned so much there, by doing and learning from an <a href="https://www.marcusoft.net/2020/09/role-models-lars-littorin.html">IT-architect called Lars</a>. He taught me a lot about consulting too.</p>

<h2 id="the-agile-revolution">The agile revolution</h2>

<p>At this point, 2004-ish I got the opportunity to take the Scrum-master course (given by Ken Schwaber, the inventor of Scrum) and it was an epiphany for me. I went in one man and came out another. I could never go back. And I never have.
Practicing agile in those early days meant that you had to learn from one day to another. I started to read blogs. A LOT. Back then just about all people agile were also developers (which is probably a great thing still today for agilist). That meant that I learned a lot about how to write software in a way that can be worked on in an agile way. It opened my eyes for testing, TDD and modular architecture. But also Continuous Integration, BDD/Specification by example and many more things.
I learned here from great people around me (Thank you Jens, Joakim, Christoph and many more)</p>

<p>At this point I started this blog (2006). And I wrote like crazy. As a way to remember. For me. But I realized that people started to read my stuff too. And I got questions and suggestions. So I learned by sharing with others. Also, in 2005 I started to work for Avega - an absolute learning factory. I enjoyed every second there, and was given a lot of room to learn, teach and apply my thoughts.</p>

<p>This put me in a spot where learning was really part of my job. I also was given a great opportunity to share my stumbling blocks with others in many different forms. At the end as a coach with others in the company as my daily job. I picked up new things everyday. I failed and tried again.</p>

<p>A LOT of reading happened now, blogs and twitter were my hangouts. But now YT has taken over as my main source of inspiration. SO. MANY. Tutorials. I especially like, don’t tell anyone, doing tutorials for things that I already knew. Especially katas and TDD-tutorials. Just typing along. <a href="https://www.marcusoft.net/2016/08/thank-you-rob.html">Rob Conery</a>, <a href="https://dannorth.net/blog/">Dan North</a>, <a href="https://www.hanselman.com/blog/">Scott Hanselmann</a>, <a href="https://gojko.net/posts.html">Gojko Adzic</a>, <a href="https://ronjeffries.com/">Ron Jeffries</a>, <a href="https://woodyzuill.com/blog/">Woody Zuill</a>, <a href="https://haacked.com/archives/">Phil Haack</a> were (and ARE) a few of my heroes.</p>

<p>During this time I started to share and speak at user groups, conferences and different company gigs. Live coding was done on more than one occasion and I can tell you; pressure has never been felt until you get a compilation error you’ve never seen on stage in front of an audience that all know what happened.</p>

<p>I was early in another consultancy started by some Avega-friends called Aptitud. We built learning new things into our normal way of working and it escalated my domains further.</p>

<h2 id="indonesia">Indonesia</h2>

<p>One fun, but also telling episode, was that I moved abroad to work for the Salvation Army in 2013. On my last day at the Aptitud office I realized that I hadn’t packed any programming books, so looked around the office. I saw <a href="https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742">“JavaScript - The Good Parts”</a> and brought it. I didn’t know any JavaScript at this point.</p>

<p>This was the start of another love story for me. That single book led me to learn not only JavaScript, but also functional programming, Node, and a whole new ecosystem. I kept blogging about my experiences and eventually I got to do a <a href="https://www.pluralsight.com/authors/marcus-hammarberg">few online course on somethings I learned</a>. I recorded them in a closest in Indonesia… you can hear the prayer calls in the background.</p>

<p>Learn and share to learn and share, or how was it.</p>

<h2 id="making-a-school">Making a school</h2>

<p>When I got back from Indonesia I eventually got offered to create a bootcamp for developers, trying to teach people with little programming experience to learn how to code in 3 months. Together with an absolutely brilliant developer and consultant called Jacob, I put together the most intense learning I’ve ever seen.</p>

<p>It turned out that packing you learnings together like that is a real challenge since a lot of the things I know are superseded by new ways, tools, and technologies. But it turned out really well and we ended up running it for 5 years, before I had to quit due to mental health reasons. 800+ people have gone through that training and I meet them in the industry just about every week. Some of them have jobs I could only dream of.</p>

<h2 id="ai-coding">AI coding</h2>

<p>With the advent of AI coding my workflow has been rocked and things that I really enjoy doing are now not important (refactoring and writing tests are good examples). The tools are starting to do that better than me, as long as I prompt it together.
This is a weird feeling; on one hand I’m worrying that what I know will be irrelevant, on the other I have never enjoyed building things more than now. I can build things that I couldn’t but I worry about not knowing the code.</p>

<p>My approach, when I’ve done it to scale, is to iterate the specifications (in text, or code) with an AI, then ask it to build it for me. I also ask it to build tests. Once the feature works - I refactor the crap out of the code; breaking it up into more component, testable functions. By refactoring I learn. And since I have tests I can fearlessly refactor the solution and be sure that it still works.</p>

<p>It might not be faster in the end, but I have control and understanding of what I have built, which I think cannot be underlined enough. Even though an AI wrote the code I am still responsible for it. “The AI wrote it” will not suffice when a feature fails. You should still care about the code.</p>

<p>If you want to be really great at something, understand how it works one level deeper. If you want to be a great C-developer you need to understand how assembly work. If you want to be a great software engineer by prompting you need to understand how JavaScript/TypeScript, C# or Rust works. I think. This is new to me. Please tell me more on how you learn.</p>

<h2 id="so---how-did-i-learn-to-code">So - how did I learn to code?</h2>

<p>I’ve (without thinking) used a three part solution:</p>

<ol>
  <li>I’ve read, watched and listened to people. A lot. Everyday. And still do. Everyday.</li>
  <li>I have been lucky to work with people better than me. You can too - just reach out to the person next to you and start to learn. Turns out - we are all good on different things. Learn from each other.</li>
  <li>Which brings me to the last part - share. When someone asks, or just when you have learned something. Share! Write about it somewhere and post. Most people don’t share. You’ll find that by sharing you will learn. It’s harder to express your thoughts in a manner that someone else can read. And then you become a person that someone (see bullet 1) read, watch or listen too. Now they learn, and you will learn from that…</li>
</ol>

<p>This blog post is an example of those 3 steps put into action - I learned from the team that shared with me. They are amazing, so I learned from them. And then I shared with y’all.</p>

<p>I hope you found this somewhat useful. Thanks a lot for reading and for sharing your journeys with me, which inspired this post.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="Programming" /><category term="Life of a consultant" /><summary type="html"><![CDATA[I asked a few people in the Umain office that are relatively new to programming how they learn in the age of AI. And they graciously shared. And I learned A LOT from that. It’s a VERY different world now, picking up programming when AI has “always” been around. You have to actively disregard help from the tools, to learn. And actively go into learning mode to actually learn. Since they shared with me it’s not more than right that I do the same. Sorry, this will be long - I’m old.]]></summary></entry><entry><title type="html">Going ‘device naked’ to meetings</title><link href="https://www.marcusoft.net/2026/01/naked-meetings.html" rel="alternate" type="text/html" title="Going ‘device naked’ to meetings" /><published>2026-01-20T04:00:00+00:00</published><updated>2026-01-20T04:00:00+00:00</updated><id>https://www.marcusoft.net/2026/01/naked-meetings</id><content type="html" xml:base="https://www.marcusoft.net/2026/01/naked-meetings.html"><![CDATA[<p>This is a post that could end up causing me problems—or at least being flagged as “not safe for work”—but here we go:</p>

<p><code class="language-plaintext highlighter-rouge">I've started going naked to meetings!</code></p>

<p>To clarify a bit, I’ve started to go <em>device</em> naked to meetings. In other words - I try, hard, to not bring any of my devices to meetings. If possible. It is hard work being device naked, I can tell you.</p>

<p>Any other interpretation would render disgust and probably me without a job. The social code in Sweden doesn’t really allow for your dirty thoughts, dear reader.</p>

<p>What I’m writing here is purely for me and I’m only going to relate my own reasons and experiences but maybe you find some value in this post.</p>

<!-- excerpt-end -->

<h2 id="the-problem">The problem</h2>

<p>I’m old and have been doing this for a long time. But with the rule of chats over emails, AI agents that do my heavy code lifting and note taking etc. I find myself being interrupted and disturbed more and more in meetings.</p>

<p>It has got increasingly worse the last year or so. If I bring my computer (“to take note on”) I inevitably end up reading an article, writing a Slack or even coding. Rarely related to the meeting. There. I’ve said it.</p>

<p>In fact, it’s leaking into my private life where I found (I’ve stopped) myself reading news during rehearsals, or unable to watch a movie without IMDBs trivia section open.</p>

<p>It has to stop. Drastic measures are needed. I’m going naked!</p>

<h2 id="going-device-naked">Going device naked</h2>

<p>Yes. I will not bring a device. Not even the phone, if I can avoid it (see <a href="#challenges">challenges</a> below), to meetings.</p>

<p>I will bring myself, my intellect and my full attention and engagement of the topic at hand. I’ve already started and found some great changes.</p>

<p>In meetings, I will keep my Work in Process limit to 1; whatever is being talked about</p>

<h2 id="benefits">Benefits</h2>

<p>What I’ve noticed already is that meetings are more interesting than before. I seem to remember more details, (yes, without taking notes) and also <em>see</em> people.</p>

<p>That last part is embarrassing but sure, I’ve noticed others before. But now that I listen - actually focused - on what they say and do, I connect with them better.</p>

<p>Hopefully my <del>nakedness</del> engagement  will also benefit others, as they have more of my attention.</p>

<h2 id="challenges">Challenges</h2>

<p>Here’s some of the challenges I’ve stumbled into, and that you might encounter if you try to be device naked.</p>

<h3 id="others">Others</h3>

<p>People will look weirdly at you. I’ve actually met a fellow device nudist (who asked to be anonymous) that was met with a comment like; <code class="language-plaintext highlighter-rouge">Oh? No computer? So you are not working in this meeting then?</code></p>

<p>Well, the hope is that it will be an even better meeting, without a computer screen.</p>

<h3 id="taking-clothes-on-but-then-off-again">Taking clothes on, but then off again</h3>

<p>The first challenge I stumbled into was that I needed my computer or phone. To look up something we need in the meeting, to take notes or to present.</p>

<p>Being device naked is not a rule, it’s more of a guideline.</p>

<p>I do what I have to and the … gulp … close the lid of the computer again. Without looking at Slack. I’ll do that after the meeting.</p>

<h3 id="remote-nakedness">Remote nakedness</h3>

<p>Most of the meetings that we have today have one or more person being remote. And being naked remotely is really awkward. Being <em>device</em> naked remote is impossible.</p>

<p>What I’ve found is that having the computer away from me helps from me switching over to another window and coding some unit tests.</p>

<p>I try to avoid having the computer in my lap. I put the phone on another table.</p>

<h3 id="notes">Notes</h3>

<p><code class="language-plaintext highlighter-rouge">Aha! But how do you take notes then?</code>, someone asked in the last meeting.</p>

<p>The short answer is that I don’t really know. But I see two potential solutions.</p>

<p>1) I take fewer, but focused notes. See <a href="#taking-clothes-on-but-then-off-again">taking clothes on</a>. Or I might use paper. If I’m the notetaker I try to make use of the Focus mode that most OS have built in. I even shut down other apps down.</p>

<p>2) There are so many great note-taking tools now. Especially in meeting tools, they are becoming increasingly good. The best I’ve seen is the one found in Notion, but you do you.</p>

<p>Then you can put the computer on the side, to take notes for you - while you enjoy the device nakedness and focus. Then spend a few minutes after the meeting picking out the most important things.</p>

<h3 id="but-but-but---im-super-busy">But, but, but - I’m super busy</h3>

<p>(No, your dirty mind ran to <code class="language-plaintext highlighter-rouge">butt</code>. Your mind. Not mine.)</p>

<p>There are some people around me that are engaged in A LOT of things. I see them buzzing around to do different things. Sometimes I’m one of them.</p>

<p>But me being unengaged in a meeting because I’m responding to Slack messages, or writing a comment on document or fixing a Slide - all of those things are not helping this meeting.</p>

<p>It is actually better to skip the meeting, if you cannot be there fully, or attend for awhile, than being half-committed.</p>

<p>You know what, the slides I created during the meeting also suffered from me not focusing on that.</p>

<p>Small burst of focus is better than big bursts of half-ass:ing it.</p>

<h2 id="summary">Summary</h2>

<p>Being device naked is scary at first. And it will feel weird, and you will fail and fall into going full device clothed again.</p>

<p>But I see this effort as me (and you?) giving each other the gift of our full attention. This will lead to better interactions and outcomes of those interactions.</p>

<p>Remember, the goal is not to be device naked. The goal is to have a good interaction.</p>

<p>It has too, right? Otherwise I’m running around naked here for no reason.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="Agile" /><category term="Programming" /><category term="Life of a consultant" /><summary type="html"><![CDATA[This is a post that could end up causing me problems—or at least being flagged as “not safe for work”—but here we go: I've started going naked to meetings! To clarify a bit, I’ve started to go device naked to meetings. In other words - I try, hard, to not bring any of my devices to meetings. If possible. It is hard work being device naked, I can tell you. Any other interpretation would render disgust and probably me without a job. The social code in Sweden doesn’t really allow for your dirty thoughts, dear reader. What I’m writing here is purely for me and I’m only going to relate my own reasons and experiences but maybe you find some value in this post.]]></summary></entry><entry><title type="html">Making the change easy</title><link href="https://www.marcusoft.net/2026/01/make-the-change-easy.html" rel="alternate" type="text/html" title="Making the change easy" /><published>2026-01-12T04:00:00+00:00</published><updated>2026-01-12T04:00:00+00:00</updated><id>https://www.marcusoft.net/2026/01/make-the-change-easy</id><content type="html" xml:base="https://www.marcusoft.net/2026/01/make-the-change-easy.html"><![CDATA[<p>One of Kent Beck’s many great things to consider is this little mind-bender of a quote</p>

<blockquote>
  <p>for each desired change, make the change easy (warning: this may be hard), then make the easy change</p>
</blockquote>

<p>The quote is on <a href="https://x.com/KentBeck/status/250733358307500032">X</a> but I first read it in his great little book <a href="https://www.amazon.se/-/en/Tidy-First-Personal-Exercise-Empirical/dp/1098151240">Tidy First?</a>. Yes, the question mark should be in there. Read the book for more.</p>

<p>Ok. This quote forces you to think for awhile, but is very good practical advise for any software developer I wanted to relate a story from last week where I used it.</p>

<!-- excerpt-end -->

<h2 id="the-problem-at-hand">The problem at hand</h2>

<p>I have built an application for myself. But then people started to ask me to use it. The problem was that I had built it as a single-user system; no notion of users in the code and using <a href="https://sqlite.org/">SQLite</a> using <a href="https://github.com/WiseLibs/better-sqlite3">better-sqlite3</a>.</p>

<p>I tackled the first <del>problem</del> improvement opportunity by simply adding a <code class="language-plaintext highlighter-rouge">user_id</code> in each table, adding login via Firebase and tweaking the entire code base. That was just a lot of work, but quite simple. Thank God for an agent that could push through it for me.</p>

<p>But the other improvement opportunity was a bit more challenging.</p>

<h2 id="the-challenge">The challenge</h2>

<p>Since I hadn’t designed the system for anything else than SQLite I had been a bit sloppy and there were database code (SQL-queries) in a few different place. Also I was using the better-sqlite3 abstractions in other placing, passing around a <code class="language-plaintext highlighter-rouge">Database</code>-reference.</p>

<p>Well, I thought to myself - that’s a lot of places, but surely another agent can just fix that for me.</p>

<p>It could. But it caused a horrible mess. Not because it failed, but it was touching too many things at the same time.</p>

<p>The SQL needed to be updated (the dialects differs a bit), the <code class="language-plaintext highlighter-rouge">Database</code> abstraction from <code class="language-plaintext highlighter-rouge">better-sqlite3</code> doesn’t behave in the same way as the PostGres (<code class="language-plaintext highlighter-rouge">pg</code>) abstractions.</p>

<p>All in all - the change was not at all easy. The AI agents could not save me. It was not just <code class="language-plaintext highlighter-rouge">a lot of work</code>, but complex and tricky.</p>

<h2 id="the-approach">The approach</h2>

<p>This was when Mr Beck’s wise word above hit me like a brick wall. How can I <code class="language-plaintext highlighter-rouge">make this change easy</code>.</p>

<p>(There’s no big innovation here, it’s just my experience. Get your expectation on the right level, people)</p>

<p>I decided to implement an Adapter with a shared interface. Like this, for example:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">type</span> <span class="nx">DatabaseProviderName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">sqlite</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">postgres</span><span class="dl">"</span><span class="p">;</span>

<span class="k">export</span> <span class="kr">interface</span> <span class="nx">DbAdapter</span> <span class="p">{</span>
  <span class="nx">query</span><span class="o">&lt;</span><span class="nx">T</span> <span class="o">=</span> <span class="nx">unknown</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">sql</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">?:</span> <span class="nx">unknown</span><span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">[]</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nx">queryOne</span><span class="o">&lt;</span><span class="nx">T</span> <span class="o">=</span> <span class="nx">unknown</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">sql</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">?:</span> <span class="nx">unknown</span><span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span> <span class="o">|</span> <span class="kc">null</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nx">execute</span><span class="p">(</span><span class="nx">sql</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">?:</span> <span class="nx">unknown</span><span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="k">void</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nx">transaction</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">fn</span><span class="p">:</span> <span class="p">(</span><span class="nx">db</span><span class="p">:</span> <span class="nx">DbAdapter</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nx">close</span><span class="p">():</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="k">void</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nl">databaseProvider</span><span class="p">:</span> <span class="nx">DatabaseProviderName</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I then implemented a <code class="language-plaintext highlighter-rouge">SQLiteAdapter</code> that implemented this interface.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">Database</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">better-sqlite3</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">readFileSync</span><span class="p">,</span> <span class="nx">mkdirSync</span><span class="p">,</span> <span class="nx">existsSync</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">node:fs</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">join</span><span class="p">,</span> <span class="nx">dirname</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">node:path</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">DbAdapter</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./DbAdapter</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">DatabaseProviderName</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">..</span><span class="dl">"</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">class</span> <span class="nx">SqliteAdapter</span> <span class="k">implements</span> <span class="nx">DbAdapter</span> <span class="p">{</span>
  <span class="k">private</span> <span class="k">readonly</span> <span class="nx">db</span><span class="p">:</span> <span class="nx">Database</span><span class="p">.</span><span class="nx">Database</span><span class="p">;</span>
  <span class="k">public</span> <span class="k">readonly</span> <span class="nx">databaseProvider</span><span class="p">:</span> <span class="nx">DatabaseProviderName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">sqlite</span><span class="dl">"</span><span class="p">;</span>
  <span class="kd">constructor</span><span class="p">(</span><span class="k">private</span> <span class="k">readonly</span> <span class="nx">dbPath</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">ensureDatabaseDirectory</span><span class="p">();</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">db</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Database</span><span class="p">(</span><span class="nx">dbPath</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">pragma</span><span class="p">(</span><span class="dl">"</span><span class="s2">foreign_keys = ON</span><span class="dl">"</span><span class="p">);</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">ensureSchema</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="c1">// ------------------------</span>
  <span class="c1">// Initialization helpers</span>
  <span class="c1">// removed for brevity</span>
  <span class="c1">// ------------------------</span>
  <span class="k">private</span> <span class="nx">ensureDatabaseDirectory</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/*...*/</span> <span class="p">}</span>
  <span class="k">private</span> <span class="nx">ensureSchema</span><span class="p">()</span>  <span class="p">{</span> <span class="cm">/*...*/</span> <span class="p">}</span>

  <span class="c1">// ------------------------</span>
  <span class="c1">// DbAdapter implementation</span>
  <span class="c1">// ------------------------</span>

  <span class="k">async</span> <span class="nx">query</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">sql</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="nx">unknown</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">[]</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">prepare</span><span class="p">(</span><span class="nx">sql</span><span class="p">).</span><span class="nx">all</span><span class="p">(</span><span class="nx">params</span><span class="p">)</span> <span class="k">as</span> <span class="nx">T</span><span class="p">[];</span>
  <span class="p">}</span>

  <span class="k">async</span> <span class="nx">queryOne</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">sql</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="nx">unknown</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span> <span class="o">|</span> <span class="kc">null</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">prepare</span><span class="p">(</span><span class="nx">sql</span><span class="p">).</span><span class="kd">get</span><span class="p">(</span><span class="nx">params</span><span class="p">)</span> <span class="k">as</span> <span class="nx">T</span><span class="p">)</span> <span class="o">??</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">async</span> <span class="nx">execute</span><span class="p">(</span><span class="nx">sql</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="nx">unknown</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="k">void</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">try</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">prepare</span><span class="p">(</span><span class="nx">sql</span><span class="p">).</span><span class="nx">run</span><span class="p">(</span><span class="nx">params</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Error executing SQL:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
      <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">async</span> <span class="nx">transaction</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">fn</span><span class="p">:</span> <span class="p">(</span><span class="nx">db</span><span class="p">:</span> <span class="nx">DbAdapter</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">trx</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">transaction</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="nx">fn</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">resolve</span><span class="p">).</span><span class="k">catch</span><span class="p">(</span><span class="nx">reject</span><span class="p">);</span>
      <span class="p">});</span>
      <span class="nx">trx</span><span class="p">();</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="k">async</span> <span class="nx">close</span><span class="p">():</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="k">void</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now the <code class="language-plaintext highlighter-rouge">simple change</code> was to move all database access code to use this interface.</p>

<p>Turned out - that was hard (remember the quote <code class="language-plaintext highlighter-rouge">(warning: this may be hard)</code>). As in complicated by not that complex. There was A LOT (I’m ashamed) of places to change, but each place was pretty simple.</p>

<p>I had two tools at my disposal that was essentials</p>

<h3 id="lean-on-the-compiler">Lean on the compiler</h3>

<p>First I was using TypeScript, which means that I had a compiler to help me.</p>

<p>When I change a reference from a <code class="language-plaintext highlighter-rouge">import Database from 'better-sqlite3';</code> to my own <code class="language-plaintext highlighter-rouge">DbAdapter</code> deep down in the nitty gritty of the code, I got a lot of compilation errors.</p>

<p>This is a Good Thing (tm). Compilation errors is a very short feedback loop and fixing each of them is often relatively straight forward.</p>

<h3 id="automated-tests">Automated tests</h3>

<p>The second thing I had in place was a bunch (700+) of automated tests. This means that I could know if the changes made broke the code as I went through each route or function.</p>

<p>Since my <code class="language-plaintext highlighter-rouge">SqliteDbAdapter</code> was the only adapter being used, I would expect the SQL-queries to behave in the same way.</p>

<p>So in a way - the change was <code class="language-plaintext highlighter-rouge">easy</code> but it was hard work. Took me almost 3 days to get through.</p>

<h2 id="after-making-the-change-easy">After making the change easy</h2>

<p>But once I was done implementing the DbAdapter throughout the codebase I was now in a much better place: the next change was now <code class="language-plaintext highlighter-rouge">easy</code>.</p>

<p>Meaning; all I had to do now was to implement a <code class="language-plaintext highlighter-rouge">PostGresDbAdapter : DbAdapter</code> and then start to use it, rather than the <code class="language-plaintext highlighter-rouge">SqliteDbAdapter</code>.</p>

<p>That was actually just one single row to change, but it caused a lot of problems (SQL didn’t work, I had to setup a local database via Docker etc. etc.)</p>

<p>BUT - each of those steps was now easier.</p>

<h2 id="and-with-that">And with that</h2>

<p>Often when faced with challenge we think that we can only do it in one go; making all the changes at once. I think this trap is especially easy to fall into with AI Agents doing the heavy lifting for us.</p>

<p>It’s easy to think that if <code class="language-plaintext highlighter-rouge">I just let it run a bit more it will eventually come through with a solution</code>.</p>

<p>But just as with humans breaking the down the problem into smaller pieces will make each step not only easier, but also less risky.</p>

<p>Much of the work we do is shaping the work we need to do. Even more so with agents doing the actual work.</p>

<p>Hence;</p>

<blockquote>
  <p>for each desired change, make the change easy (warning: this may be hard), then make the easy change</p>
</blockquote>]]></content><author><name>Marcus Hammarberg</name></author><category term="Flow" /><category term="Programming" /><category term="Life of a consultant" /><summary type="html"><![CDATA[One of Kent Beck’s many great things to consider is this little mind-bender of a quote for each desired change, make the change easy (warning: this may be hard), then make the easy change The quote is on X but I first read it in his great little book Tidy First?. Yes, the question mark should be in there. Read the book for more. Ok. This quote forces you to think for awhile, but is very good practical advise for any software developer I wanted to relate a story from last week where I used it.]]></summary></entry><entry><title type="html">On Yak shaving</title><link href="https://www.marcusoft.net/2025/12/on-yak-shaving.html" rel="alternate" type="text/html" title="On Yak shaving" /><published>2025-12-17T04:00:00+00:00</published><updated>2025-12-17T04:00:00+00:00</updated><id>https://www.marcusoft.net/2025/12/on-yak-shaving</id><content type="html" xml:base="https://www.marcusoft.net/2025/12/on-yak-shaving.html"><![CDATA[<p><a href="https://www.umain.com/insights/on-yak-shaving">Re-posted from Umain</a>.</p>

<p>As I entered the office I found a seat next to a nice coworker in the quiet corner. Perfect!</p>

<p>Only thing… that screen didn’t have a USB-C cable. No worries I’ll run down to the IT department and nick one.</p>

<p>Only thing… once I got back up I realized that the screen didn’t even have a power cable. Back down to IT, but this time I checked and yes - there were missing extension cords too. So I asked for that.</p>

<p>Once back at my desk I now had to unplug (and UNTANGLE) the cords, so that I could plug in the other 3 desks, interrupting my coworkers flow 2 times. And then connect my own desk to power.</p>

<p>Now I could attach the power cord. And then the USB-C. And then connect my computer so that I could … what was I doing now again.
<!-- excerpt-end --></p>

<p><img src="/img/yak_to_shave.jpg" alt="Yak to Shave" /></p>

<h2 id="yak-shaving-defined">Yak shaving defined</h2>

<p>Recognize this? It happens a lot in daily work - spend (an unreasonable amount) of time doing something that is hard, tricky and often boring in order to be able to do the work we were here to do in the first place</p>

<p>This, my friend, is known as Yak-shaving and has been plaguing worker generally and programmers specifically, for as long as work has happened. The term was, probably, coined by Carlin Vieri at MIT (of course - anything IT-related seems to stem from there) and has a tighter definition that goes like this:</p>

<blockquote>
  <p>Any apparently useless activity which, by allowing one to overcome intermediate difficulties, allows one to solve a larger problem.</p>
</blockquote>

<h2 id="yak-etymology">Yak etymology</h2>

<p>This section is written from hearsay, but I kind of love the story.</p>

<p>Apparently (^^) the reason yak shaving was used for this type of activity is due to an activity in managing actually yaks. Yes - those half-tonne cows that people in the Himalayas are using.</p>

<p>They are big, smelly and have a thick fur. The yak herders (hey - that’s a job title if I ever saw one) take the yaks up to the mountains in the summer where they graze the lovely green grass.</p>

<p>Now, that means that they fatten up, and when the herders take them down the yaks do not fit on the tiny bridges that go over high pass and rivers.</p>

<p>The herders then have to shave the thick fur off. It’s hard work, takes a lot of time and they cannot really do anything with the fur as it lies in the mountains. But they have to do it to get the Yak down.</p>

<p>That’s the original yak shaving story. I think.</p>

<h2 id="yak-navigation">Yak navigation</h2>

<p>Ok, but seems that there are a lot of Yaks to shave in the world and it might be hard to know how to do that properly, and even which yaks to shave and which one to leave hairy.</p>

<p>I remember a brilliant lightning talk from Gabriel Forsberg at Agile Sweden 2013 called “Yak shaving the right way”. And I wanted to share his findings here, as I remember them, and add my own comments to it. Any comic relief goes to Gabriel.</p>

<p>There are 3 things to consider when faced with a yak to shave:</p>

<ol>
  <li>Shave the right yak</li>
  <li>Shave it well</li>
  <li>Share the yak-shaving</li>
</ol>

<h3 id="shave-the-right-yak">Shave the right yak</h3>

<p>When faced with a yak you are first overwhelmed with the size and the amount of fur that needs to shaven. It can, for that reason, be worth some reflection time and consider - is this even the right yak.</p>

<p>For example, when I came to the desk, I first thought about if these desks are going to be around here for awhile, or if it was just temporary. I could easily have switched to another place, or just unplugged another screen if these were temporary desks.</p>

<p>The time it will take is also something to consider. This can be hard, as <code class="language-plaintext highlighter-rouge">we often don’t discover the work we need to do until we are doing the work</code> (<a href="https://woodyzuill.com/">Woody Zuill</a> quote). Or, I guess, we don’t see how thick the yak hair is until we start to shave it.</p>

<p>This conundrum can be overcome with time boxing. I, actually, checked my calendar before starting and realized that I had time to fix the desk (shave the yak).</p>

<p>Another consideration is to ensure that you are not just shaving the yak for your own pleasure. Hairless yaks are ugly and they don’t like being shaved - if you are going to shave a yak, please let it be for others enjoyment too.</p>

<p>In my case; yes - other will benefit from me solving this problem properly.</p>

<h3 id="shave-it-well">Shave it well</h3>

<p>Once you have decide to shave the yak, don’t mess around. Shave it, and shave it well. A job worth doing is worth doing well.</p>

<p>In my case, I could probably have got it to work by borrowing some outlets that are on top of the table, and have used only one extension cord to connect all tables. But then the extensions on the table top would not have been useable for charging stuff with, as intended. And by entangling the cords the table might not be possible to raise.</p>

<p>Once I was done, I spent one extra minute to check that everything worked. It’s easier to fix as I’m in my shaving mode than to go back later, bring out the razor and protective gear again… (am I taking the metaphor too far?)</p>

<p>Do the work so that no one else has to do it again. No one should have to shave this particular yak again. In the metaphorical world of yak-shaving yaks, hair typically don’t grow out as fast as on real life yaks.</p>

<p>Once you see the well-shaven yak in front of you, a sense of pride will fill you. Something better is in place in the world, thanks to you. You want to tell others - hey that’s the last step.</p>

<h3 id="share-the-yak-shaving">Share the yak-shaving</h3>

<p>Just shaving a yak, the right yak well, so that others don’t have to, might not be enough to ensure that others don’t accidentally shaves the yak again.</p>

<p>This can be bragging (<code class="language-plaintext highlighter-rouge">Hey guys! Look at me - I shaved this Yak. ALL. BY. MYSELF!</code>) but the tone of your announcement can help that.</p>

<p>In my case, fixing the cords for a screen is not much to talk about. My hope is that the next person that sits here, just don’t have to get cord. I presume that I’ll tell IT, they might have this on their todo-list. But other than that I’ll leave it.</p>

<p>I did, however, write this blog post to let the WORLD, know about yak shaving and hopefully giggle a bit. Funnily enough, in writing this post, I had to look up some facts on yaks, how to spell etymology , find a yak picture and many other things not related to my work. Meta-yak-shaving.</p>

<h2 id="summary">Summary</h2>

<p>Yak shaving is part of life for all of us. But remember the three golden rules of yak shaving:</p>

<ol>
  <li>Shave the right yak</li>
  <li>Shave it well</li>
  <li>Share the yak-shaving</li>
</ol>]]></content><author><name>Marcus Hammarberg</name></author><category term="Flow" /><category term="Life of a consultant" /><summary type="html"><![CDATA[Re-posted from Umain. As I entered the office I found a seat next to a nice coworker in the quiet corner. Perfect! Only thing… that screen didn’t have a USB-C cable. No worries I’ll run down to the IT department and nick one. Only thing… once I got back up I realized that the screen didn’t even have a power cable. Back down to IT, but this time I checked and yes - there were missing extension cords too. So I asked for that. Once back at my desk I now had to unplug (and UNTANGLE) the cords, so that I could plug in the other 3 desks, interrupting my coworkers flow 2 times. And then connect my own desk to power. Now I could attach the power cord. And then the USB-C. And then connect my computer so that I could … what was I doing now again.]]></summary></entry><entry><title type="html">Some thoughts on lead time in the age of AI</title><link href="https://www.marcusoft.net/2025/11/ai-and-lead-times.html" rel="alternate" type="text/html" title="Some thoughts on lead time in the age of AI" /><published>2025-11-27T04:00:00+00:00</published><updated>2025-11-27T04:00:00+00:00</updated><id>https://www.marcusoft.net/2025/11/ai-and-lead-times</id><content type="html" xml:base="https://www.marcusoft.net/2025/11/ai-and-lead-times.html"><![CDATA[<p>Over and over I see that in the paradigm shift we are in now, the good old practices keep resurfacing. For example: doing BDD and TDD turns out to be a good idea even if the code is written much faster—and often not by you. Or having a solid understanding of how to create a good, well‑structured engineered application is more important now than ever.</p>

<p>There are more things, but I’ll get to that at the end.</p>

<p>In this post I wanted to share some thoughts about lead time that I cannot get out of my head. Let’s go back to the beginning by me sharing the simplest diagram I can think of, presented here in my handwriting from 2018, first shown in a <a href="https://www.marcusoft.net/2018/03/a-simple-diagram-on-flow-efficiency.html">blog post on flow efficiency</a>.</p>

<!-- excerpt-end -->

<p><img src="/img/flowefficiency_2.jpg" alt="Flow of Work" /></p>

<p>This is a very crude version of a <a href="https://en.wikipedia.org/wiki/Value_stream_mapping">value stream map</a>, but I’ve always found it useful. Even just presenting it like above is typically enough to get people’s attention.</p>

<p>A few things that we need to point out, because sometimes I do a poor job of explaining it:</p>

<ol>
  <li>This is the lifetime of a single feature. The work or waiting is when the <strong>feature</strong> is waiting for someone to work on it. This diagram follows the <em>work</em>, not the <em>workers</em>.</li>
  <li>Obviously (but also problematically) I’ve seldom met anyone who puts their feet up when faced with a waiting time. Instead we start a new thing. That soon gets blocked like the first feature, so we start yet another thing. Then the first thing comes back and requires our attention. And then also—what about the second thing; could you take a look at it now? More things going on at the same time will actually hurt the focus and flow of the original feature. But it’s a totally reasonable way to behave—I would probably do the same.</li>
</ol>

<p>With that out of the way—let’s do some math-y things. There’s a metric known as flow efficiency that is interesting to observe. It’s the <code class="language-plaintext highlighter-rouge">time we work</code> divided by the <code class="language-plaintext highlighter-rouge">total time</code> to complete the work (feature in this case).</p>

<p>You might be surprised to know that for most processes (that have not been optimized for flow) the flow efficiency is… 10–15%. Only 48 minutes of actual work is done on something that took 8 hours before it was delivered.</p>

<p>The time from “Could you please” to “Thank you” is known as lead time. It’s not until we are done with the entire feature that our <a href="https://www.marcusoft.net/2019/09/when-was-lars-happy">customer is happy</a>.</p>

<h2 id="improving-output--original-observation">Improving output – original observation</h2>

<p>My original post observed that we spend a lot of time improving the speed with which we work, trying to do things faster. Which is a worthy goal, but we are not really making a huge impact on the overall time to complete the work.</p>

<p><img src="/img/flowefficiency_4.jpg" alt="HUGE Improvement in output" /></p>

<p>See?! Totally amazing—we cut that step to 1/5 of the original. But it barely matters for the overall lead time. Also note: since we didn’t do anything about the waiting times, the flow efficiency is actually worse. We wait more of the total time.</p>

<p>Instead we need to do something about those pesky waiting times, which is a different story altogether. That requires us to work differently (for example, consider using mob programming where all the waiting times are obliterated, as well as some of the working stages) and also steer differently.</p>

<p>For example: one of the things we wait for could be related to getting approval from a stakeholder, or even getting input on what we should build. To remove that waiting time we need the team to know what to build and own the feature so that they can approve things themselves.</p>

<h2 id="improved-output--in-the-age-of-ai">Improved output – in the age of AI</h2>

<p>Earlier when I talked about this, I sometimes had a hard time coming up with how we could move faster in some areas. Better refactoring tools? Someone who types faster? Automated tests?</p>

<p>With the advent of the AI tools we can now see that not only some of the parts of the value chain can produce output much faster, but all of them. Design, writing requirements, coding, testing—there’s AI everywhere. And all of it helps us work faster. We get the output faster.</p>

<p><img src="/img/flowefficiency_7.jpg" alt="AI helps us in many steps - with output" /></p>

<p>Which means that the flow efficiency is even worse than before. Now 99.4% (&lt;= Marcus makes numbers up to prove a point) of a feature’s lifetime is spent waiting.</p>

<h2 id="disclaimer">Disclaimer</h2>

<p>I’m the first to admit that there might be entire steps that are now redundant in some AI scenarios. Maybe we can have the AI do the code review for us and fix the problems while we do something else. Maybe the design can be translated to React components automatically. But we are not there yet. At the time of writing.</p>

<h2 id="what-to-do-instead">What to do instead?</h2>

<p>The waiting time in the lifetime of a feature is often related to how we have organized and structured the value flow. Things like our team setup, cadence, and ceremonies for planning and review. This means that in order to address the bulk of the waiting time we need to focus our attention on the things around the people doing the work—our operating model, if you will.</p>

<p>And it should be clear that more checks, more tollgates, and more manual intervention will not harness the improved speed in the output steps.</p>

<p>Instead we need to lean in, even more, to some of the values we talked a lot about in the early agile days; things like empowerment, trust, and steering with outcomes and goals rather than tasks and plans. (See—that thing from earlier in the post came back. Told you.)</p>

<p>I have a lot to say about this, but we are packing it up into something a bit more digestible, so I’ll just leave you with a few core beliefs that I think will be increasingly important in the age of AI:</p>

<ul>
  <li>Empowered teams that are allowed to move at their own pace toward goals.</li>
  <li>Teams that are trusted to do the right thing, trusted to get help when needed, and trusted to find the information they need to do the right thing.</li>
  <li>Radical transparency so that anyone in the organization can pull the information when needed.</li>
  <li>Organizations aligned not on plans and milestones, but on outcomes with priorities and shared definitions of success.</li>
  <li>Cross‑organization collaboration built in from the start, where dependencies between teams are managed to support flow of value rather than utilization of resources.</li>
  <li>Continuous improvements of our process on all levels. No more best practices—just good‑enough‑for‑now practices that we use until we find a better way. Like tomorrow.</li>
</ul>

<h2 id="summary">Summary</h2>

<p>Strap in everyone. This will be another interesting paradigm shift.</p>

<p>Improving the speed of work (output) will soon make the problems in our flow (waiting times etc.) very clear.</p>

<p>This cannot be solved with AI, but needs to be solved by unlocking the true potential of every team and individual and giving them room to be awesome.</p>

<p>Get on the Trust‑train—it’s a great ride.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="Lean" /><category term="Flow" /><category term="AI" /><summary type="html"><![CDATA[Over and over I see that in the paradigm shift we are in now, the good old practices keep resurfacing. For example: doing BDD and TDD turns out to be a good idea even if the code is written much faster—and often not by you. Or having a solid understanding of how to create a good, well‑structured engineered application is more important now than ever. There are more things, but I’ll get to that at the end. In this post I wanted to share some thoughts about lead time that I cannot get out of my head. Let’s go back to the beginning by me sharing the simplest diagram I can think of, presented here in my handwriting from 2018, first shown in a blog post on flow efficiency.]]></summary></entry><entry><title type="html">Trying out TCR (test commit or revert) - updated</title><link href="https://www.marcusoft.net/2025/10/trying-out-test-commit-or-revert-modern.html" rel="alternate" type="text/html" title="Trying out TCR (test commit or revert) - updated" /><published>2025-10-29T04:00:00+00:00</published><updated>2025-10-29T04:00:00+00:00</updated><id>https://www.marcusoft.net/2025/10/trying-out-test-commit-or-revert-modern</id><content type="html" xml:base="https://www.marcusoft.net/2025/10/trying-out-test-commit-or-revert-modern.html"><![CDATA[<p>A long time ago I wrote a <a href="https://www.marcusoft.net/2019/01/trying-out-test-commit-or-revert.html">blog post on TCR</a>, just now it came up again. I showed them the old blog post but realized that it was very outdated and wanted to update it to a modern stack.</p>

<p>Read the <a href="https://www.marcusoft.net/2019/01/trying-out-test-commit-or-revert.html">original post</a> to understand what this about. You can find <a href="https://github.com/marcusoftnet/fizzbuzz-tcr-ts">my code here</a>
<!-- excerpt-end --></p>

<h2 id="the-initialization">The initialization</h2>

<p>Here are the commands I ran to get started:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">mkdir fizzbuzz-tcr-ts &amp;&amp; cd fizzbuzz-tcr-ts</code> to create the directory and jump into it</li>
  <li><code class="language-plaintext highlighter-rouge">npm init -y</code> to create an empty <code class="language-plaintext highlighter-rouge">package.json</code> file</li>
  <li><code class="language-plaintext highlighter-rouge">npm i -D typescript vitest</code> to install the tools I need</li>
  <li><code class="language-plaintext highlighter-rouge">touch index.ts index.test.ts</code> to setup the two files we will work in</li>
  <li><code class="language-plaintext highlighter-rouge">npx scradd test "vitest run"</code> to add a test script</li>
  <li>
    <p>I then wrote the first test to check that my infrastructure worked. In the <code class="language-plaintext highlighter-rouge">index.text.ts</code>:</p>

    <div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">import</span> <span class="p">{</span> <span class="nx">describe</span><span class="p">,</span> <span class="nx">it</span><span class="p">,</span> <span class="nx">expect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">vitest</span><span class="dl">"</span><span class="p">;</span>
 <span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">Testing</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
   <span class="nx">it</span><span class="p">(</span><span class="dl">"</span><span class="s2">should work</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
     <span class="nx">expect</span><span class="p">(</span><span class="kc">true</span><span class="p">).</span><span class="nx">toBe</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
   <span class="p">});</span>
 <span class="p">});</span>
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">npm t</code> - I ran the first test</li>
  <li><code class="language-plaintext highlighter-rouge">npx gitignore node</code> - I created a <code class="language-plaintext highlighter-rouge">.gitignore</code> from the excellent <a href="https://www.gitignore.io/">https://www.gitignore.io/</a></li>
  <li><code class="language-plaintext highlighter-rouge">git init . &amp;&amp; git add . &amp;&amp; git commit -m "Initial commit"</code> Finally, I initialized git and made a first commit initial commit</li>
</ol>

<h2 id="setting-up-tcr-workflow-in-packagejson">Setting up TCR workflow in package.json</h2>

<p>In the <code class="language-plaintext highlighter-rouge">package.json</code> I wanted a single script to do the test and then commit or revert.</p>

<p>First I wrote the <code class="language-plaintext highlighter-rouge">commit</code> script like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"commit"</span><span class="p">:</span><span class="w"> </span><span class="s2">"git add -A; timestamp=$(date </span><span class="se">\"</span><span class="s2">+%c</span><span class="se">\"</span><span class="s2">) &amp;&amp; git commit -m </span><span class="se">\"</span><span class="s2">TCR @ $timestamp</span><span class="se">\"</span><span class="s2">;"</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>

<p>This will make a nice commit and add a timestamp in the git log.</p>

<p>The revert command is even simpler, but also more unforgiving</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"revert"</span><span class="p">:</span><span class="w"> </span><span class="s2">"git reset --hard"</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>

<p>Creating the final command became very simple. So simple that I didn’t know if it would work. Here’s the command:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"tcr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm test &amp;&amp; npm run commit || npm run revert"</span><span class="w">
</span></code></pre></div></div>

<p>First, the <code class="language-plaintext highlighter-rouge">tcr</code> script will run the tests and if it works it will continue to the part after the <code class="language-plaintext highlighter-rouge">&amp;&amp;</code> and do the commit. If the <code class="language-plaintext highlighter-rouge">npm test</code> fails the part after the <code class="language-plaintext highlighter-rouge">||</code> will run and revert the changes.</p>

<p>You can think about it like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>npm <span class="nb">test</span> <span class="o">&amp;&amp;</span> npm run commit<span class="o">)</span> <span class="o">||</span> npm run revert
</code></pre></div></div>

<p>That made it simpler to understand for me at least.</p>

<p>Anyway, I can now do the workflow by executing <code class="language-plaintext highlighter-rouge">npm run tcr</code>. Nice!</p>

<h2 id="the-test-runs">The test runs</h2>

<p>The following sections describe the tests runs that I did to complete the kata. For each test run I will describe the test and production code I wrote, how I felt before I ran <code class="language-plaintext highlighter-rouge">npm run tcr</code> and … yes, what happened.</p>

<p>I did not enable auto-run of this command on every save. This is of course possible, but I think it take some of the thoughtfulness out of the process and just make it stressful. TCR is about, and forces you to, thinking carefully about the next step - not going fast.</p>

<h3 id="first-test-run">First test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">describe</span><span class="p">,</span> <span class="nx">it</span><span class="p">,</span> <span class="nx">expect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">vitest</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">single</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">;</span>

<span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">FizzBuzz single number</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1" for 1</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">single</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="nx">expect</span><span class="p">(</span><span class="nx">result</span><span class="p">).</span><span class="nx">toBe</span><span class="p">(</span><span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: NERVOUS! Will it run?</p>

<p><strong>Result</strong>: Passed and commit</p>

<h3 id="second-test-run">Second test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "2" for 2</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">fizzBuzzer</span><span class="p">.</span><span class="nx">single</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
  <span class="nx">assert</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="dl">"</span><span class="s2">2</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>
    <p>Set up the whole test. Pretty sure of myself… failed and reverted.</p>
  </li>
  <li>
    <p>Cocky! This will work…</p>
  </li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>
    <p>Ah well…It failed and my changes was gone.</p>
  </li>
  <li>No production code changed… Hence I returned a constant of <code class="language-plaintext highlighter-rouge">1</code>.
    <ul>
      <li>And I even thought that <code class="language-plaintext highlighter-rouge">I didn't change any production code to get this to work... hmmm... this feels strange</code></li>
    </ul>
  </li>
  <li>Lost documentation (i.e. this blog post) too. This was the point where I decided to move the documentation from ReadMe.md in the repository to a separate blog post.</li>
</ul>

<h3 id="second-test-run---second-try">Second test run - second try</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "2" for 2</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">fizzBuzzer</span><span class="p">.</span><span class="nx">single</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
  <span class="nx">assert</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="dl">"</span><span class="s2">2</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: Careful optimistic but still held my breath during the run.</p>

<p><strong>Result</strong>: Passed and commit.</p>

<h3 id="refactoring-the-tests">Refactoring the tests</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">assertSingle</span> <span class="o">=</span> <span class="p">(</span><span class="nx">input</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">expected</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">expect</span><span class="p">(</span><span class="nx">single</span><span class="p">(</span><span class="nx">input</span><span class="p">)).</span><span class="nx">toBe</span><span class="p">(</span><span class="nx">expected</span><span class="p">);</span>
<span class="p">};</span>

<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1" for 1</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">));</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "2" for 2</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="dl">"</span><span class="s2">2</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">module</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: Very confident</p>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed and commit.</li>
</ul>

<h3 id="third-test-run">Third test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "Fizz" for 3</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="dl">"</span><span class="s2">3</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">3</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: Carefully confident and reflecting over the amount of code I wrote now… What if I lost it…</p>

<p><strong>Result</strong>:</p>

<ul>
  <li>FAILED! I asserted for ‘3’ in the test and not ‘Fizz’…</li>
  <li>Rewrote and works</li>
</ul>

<h3 id="fourth-test-run">Fourth test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "Fizz" for 3</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">3</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: Pretty confident</p>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed and commit</li>
</ul>

<h3 id="fifth-test-run">Fifth test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "Buzz" for 5</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">5</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">3</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: Very confident but no changes in production code … This <em>should</em> work</p>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed and commit</li>
</ul>

<h3 id="sixth-test-run">Sixth test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "FizzBuzz" for 15</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">3</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">===</span> <span class="mi">5</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">5</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">3</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>: Again… I felt like this was a lot of code all of a sudden</p>

<p><strong>Result</strong>:</p>

<ul>
  <li>AND BLUÄRK - it failed… because I checked for exactly 3, 5 and 3 and 5… I didn’t check for things <em>divisible</em> with 3 or 5. In fact, under TypeScript this doesn’t even compile.</li>
  <li>IDIOT - I needed more cases for Fizz and Buzz</li>
</ul>

<h3 id="seventh-test-run">Seventh test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "Fizz" for 6</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">===</span> <span class="mi">5</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Pretty nice to start over actually</li>
  <li>A bit nervous</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed</li>
</ul>

<h3 id="eight-test-run">Eight test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "Buzz" for 10</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Confident</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed</li>
</ul>

<h3 id="ninth-test-run">Ninth test run</h3>

<p>I made some refactoring here. No test changed</p>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>

</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Pretty nervous actually. 2 rows changed in one go. What if this goes wrong?!!!</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>PHEW! Still works!</li>
</ul>

<h3 id="tenth-test-run">Tenth test run</h3>

<p>More refactoring. No test changed</p>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isBuzz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">isBuzz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Pretty nervous actually. 2 rows changed in one go. What if this goes wrong?!!!</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>PHEW! Still works!</li>
</ul>

<h3 id="eleventh-test-run">Eleventh test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "FizzBuzz" for 15</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">assertSingle</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">));</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isBuzz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">isBuzz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Pretty nervous</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed.</li>
  <li>I’m done with this feature and can squash my commits into a push-able commit. I didn’t not but pressed on.</li>
</ul>

<h3 id="twelfth-test-run">Twelfth test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">FizzBuzz string</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{});</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>I just created a describe block and ran that. To commit it. That now became my mode of thinking: I need to test this so that it commits</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed.</li>
</ul>

<h3 id="thirteenth-test-run">Thirteenth test run</h3>

<p>Let’s write something that can take a start and stop and returns a full string.</p>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">describe</span><span class="p">,</span> <span class="nx">it</span><span class="p">,</span> <span class="nx">expect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">vitest</span><span class="dl">"</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">single</span><span class="p">,</span> <span class="nx">sequence</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">;</span>

<span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">FizzBuzz string</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1" for 1 to 1</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">sequence</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
    <span class="nx">expect</span><span class="p">(</span><span class="nx">result</span><span class="p">).</span><span class="nx">toBe</span><span class="p">(</span><span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code, in the same file as before:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">sequence</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>

<ul>
  <li><strong>Feeling before tcr-command</strong>: Yes. Got the nervous feeling again. There are some lines of infrastructure in there…</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed.</li>
</ul>

<h3 id="fourteenth-test-run">Fourteenth test run</h3>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1, 2, Fizz" for 1 to 3</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">sequence</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
  <span class="nx">expect</span><span class="p">(</span><span class="nx">result</span><span class="p">).</span><span class="nx">toBe</span><span class="p">(</span><span class="dl">"</span><span class="s2">1, 2, Fizz</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isBuzz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">isBuzz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">range</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span><span class="p">[]</span> <span class="o">=&gt;</span>
  <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span> <span class="na">length</span><span class="p">:</span> <span class="nx">end</span> <span class="o">-</span> <span class="nx">start</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">start</span> <span class="o">+</span> <span class="nx">i</span><span class="p">)</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">sequence</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="nx">range</span><span class="p">(</span><span class="nx">start</span><span class="p">,</span> <span class="nx">end</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">single</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">,</span><span class="dl">"</span><span class="p">);</span>
<span class="p">};</span>

</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Proud of the functional style I ended up with. Yes I had to write my own <code class="language-plaintext highlighter-rouge">range</code>-function… Should really be included</li>
  <li>Cheated (?) by testing some parts out in the REPL</li>
  <li>VERY NERVOUS about losing these beautiful lines</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>FAAAILLED. NOOOO. I took too big steps</li>
</ul>

<h3 id="fifteenth-test-run">Fifteenth test run</h3>

<p>A small space was the problem.</p>

<p>Now I needed to rewrite that code from scratch. But I took the opportunity to do so to train.</p>

<p>Here’s the updated code</p>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1, 2" for "1,2"</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">assert</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="nx">fizzBuzzer</span><span class="p">.</span><span class="kr">string</span><span class="p">(</span><span class="dl">"</span><span class="s2">1, 2</span><span class="dl">"</span><span class="p">),</span> <span class="dl">"</span><span class="s2">1, 2</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isBuzz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">isBuzz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">range</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span><span class="p">[]</span> <span class="o">=&gt;</span>
  <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span> <span class="na">length</span><span class="p">:</span> <span class="nx">end</span> <span class="o">-</span> <span class="nx">start</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">start</span> <span class="o">+</span> <span class="nx">i</span><span class="p">)</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">sequence</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="nx">range</span><span class="p">(</span><span class="nx">start</span><span class="p">,</span> <span class="nx">end</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">single</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">, </span><span class="dl">"</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Very confident now that this should work</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>And it worked</li>
</ul>

<h3 id="sixteenth-or-so-test-run---refactoring">Sixteenth (or so) test run - refactoring</h3>

<p>I’ve changed paths here from my original post. I’m using the <code class="language-plaintext highlighter-rouge">single</code> method, and then I refactored the tests a bit.</p>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">FizzBuzz string</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">assertSequence</span> <span class="o">=</span> <span class="p">(</span><span class="na">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="na">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="na">expected</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">expect</span><span class="p">(</span><span class="nx">sequence</span><span class="p">(</span><span class="nx">start</span><span class="p">,</span> <span class="nx">end</span><span class="p">)).</span><span class="nx">toBe</span><span class="p">(</span><span class="nx">expected</span><span class="p">);</span>
  <span class="p">};</span>

  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1" for 1 to 1</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">assertSequence</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">});</span>

  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns "1, 2, Fizz" for 1 to 3</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">assertSequence</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="dl">"</span><span class="s2">1, 2, Fizz</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isBuzz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">isBuzz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">range</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span><span class="p">[]</span> <span class="o">=&gt;</span>
  <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span> <span class="na">length</span><span class="p">:</span> <span class="nx">end</span> <span class="o">-</span> <span class="nx">start</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">start</span> <span class="o">+</span> <span class="nx">i</span><span class="p">)</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">sequence</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="nx">range</span><span class="p">(</span><span class="nx">start</span><span class="p">,</span> <span class="nx">end</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">single</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">, </span><span class="dl">"</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Felt nice to do the fast and frequent commits</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>Passed</li>
  <li>AND commit. I like this more and more.</li>
</ul>

<h3 id="seventh-test-run-1">Seventh test run</h3>

<p>Let’s do the full 1 to 15 and call it a day.</p>

<p>Test:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">returns the correct string for 1 to 15</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">assertSequence</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="dl">"</span><span class="s2">1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Production code is the same:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">single</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">FizzBuzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isBuzz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Buzz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">isFizz</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="k">return</span> <span class="dl">"</span><span class="s2">Fizz</span><span class="dl">"</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">isFizz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">isBuzz</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">boolean</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">5</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">range</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span><span class="p">[]</span> <span class="o">=&gt;</span>
  <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">({</span> <span class="na">length</span><span class="p">:</span> <span class="nx">end</span> <span class="o">-</span> <span class="nx">start</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">},</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">start</span> <span class="o">+</span> <span class="nx">i</span><span class="p">)</span>

<span class="k">export</span> <span class="kd">const</span> <span class="nx">sequence</span> <span class="o">=</span> <span class="p">(</span><span class="nx">start</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">end</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="nx">range</span><span class="p">(</span><span class="nx">start</span><span class="p">,</span> <span class="nx">end</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">single</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">, </span><span class="dl">"</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>

<p><strong>Feeling before tcr-command</strong>:</p>

<ul>
  <li>Confident and pretty sure this is the final implementation</li>
</ul>

<p><strong>Result</strong>:</p>

<ul>
  <li>It worked</li>
</ul>

<h2 id="summary">Summary</h2>

<p>I’m still in awe how this exercise is teaching me to take smaller steps. Every smaller. My <a href="https://github.com/marcusoftnet/fizzbuzz-tcr-ts">code is here</a>… That <a href="https://github.com/marcusoftnet/fizzbuzz-tcr-ts/commits/main/">took a lot of commits</a></p>]]></content><author><name>Marcus Hammarberg</name></author><category term="Javascript" /><category term="Typescript" /><category term="Programming" /><category term="TDD" /><summary type="html"><![CDATA[A long time ago I wrote a blog post on TCR, just now it came up again. I showed them the old blog post but realized that it was very outdated and wanted to update it to a modern stack. Read the original post to understand what this about. You can find my code here]]></summary></entry><entry><title type="html">Do something - how just get going is better than thinking</title><link href="https://www.marcusoft.net/2025/06/do-something.html" rel="alternate" type="text/html" title="Do something - how just get going is better than thinking" /><published>2025-06-12T04:00:00+00:00</published><updated>2025-06-12T04:00:00+00:00</updated><id>https://www.marcusoft.net/2025/06/do-something</id><content type="html" xml:base="https://www.marcusoft.net/2025/06/do-something.html"><![CDATA[<p>In <a href="https://www.youtube.com/watch?v=ivwKQqf4ixA&amp;t=1234s">this presentation</a>, my great inspiration <a href="https://en.wikipedia.org/wiki/David_Marquet">David L. Marquet</a>, drops a quote that I somehow hadn’t heard before:</p>

<blockquote>
  <p>[When changing our ways of working] we act our way to new thinking, rather than think our way into new ways of acting.</p>
</blockquote>

<p>This aligns well with many lean and Toyota Production System (TPS) principles. Toyota kata by Mike Rother shows how this practice is used extensively throughout Toyota.</p>

<p>I just had a mini-experience of this, where I wanted to document a pretty unwieldy conversation that was touching highs and lows in a large-ish writing undertaking we’re doing at work. Rather than start, because I didn’t know where, I thought about the best approach.</p>

<p>In my mind, each idea for how to approach this task, was presented and discarded. Then I just went ahead and did it. I opened a place where we had previously written parts of this - a FigJam Board. And then just threw in a few stickies and tried to fit it in the current structure.</p>

<p>By doing that I realized that the current structure did not only guide me to know where to put the new ideas, but also helped me formulate them. And much of the things that we had discussed in the meeting was already in there, but differently formatted.</p>

<p>Frequently I find myself trying to think my work into being done. When it would be better to just try something and see what I could learn by doing.</p>

<p>It reminds me of the first part of <a href="https://www.marcusoft.net/2024/02/do-something-small-useful-now.html">Bob Bemer’s motto</a>:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((((DO SOMETHING!) SMALL) USEFUL) NOW!)
</code></pre></div></div>

<p>It’s better to just get going and do something. If you do it small, you will soon realize if it is useful.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="Life of a consultant" /><category term="Marcus private" /><summary type="html"><![CDATA[In this presentation, my great inspiration David L. Marquet, drops a quote that I somehow hadn’t heard before: [When changing our ways of working] we act our way to new thinking, rather than think our way into new ways of acting. This aligns well with many lean and Toyota Production System (TPS) principles. Toyota kata by Mike Rother shows how this practice is used extensively throughout Toyota. I just had a mini-experience of this, where I wanted to document a pretty unwieldy conversation that was touching highs and lows in a large-ish writing undertaking we’re doing at work. Rather than start, because I didn’t know where, I thought about the best approach. In my mind, each idea for how to approach this task, was presented and discarded. Then I just went ahead and did it. I opened a place where we had previously written parts of this - a FigJam Board. And then just threw in a few stickies and tried to fit it in the current structure. By doing that I realized that the current structure did not only guide me to know where to put the new ideas, but also helped me formulate them. And much of the things that we had discussed in the meeting was already in there, but differently formatted. Frequently I find myself trying to think my work into being done. When it would be better to just try something and see what I could learn by doing. It reminds me of the first part of Bob Bemer’s motto: ((((DO SOMETHING!) SMALL) USEFUL) NOW!) It’s better to just get going and do something. If you do it small, you will soon realize if it is useful.]]></summary></entry><entry><title type="html">Lowering number input sources in process</title><link href="https://www.marcusoft.net/2025/06/lowering-input-wip.html" rel="alternate" type="text/html" title="Lowering number input sources in process" /><published>2025-06-12T04:00:00+00:00</published><updated>2025-06-12T04:00:00+00:00</updated><id>https://www.marcusoft.net/2025/06/lowering-input-wip</id><content type="html" xml:base="https://www.marcusoft.net/2025/06/lowering-input-wip.html"><![CDATA[<p>Just back from a four-week vacation (out of the <a href="https://sweden.se/work-business/working-in-sweden/work-life-balance">5 weeks mandated by law in Sweden</a>, btw. Sweden is great!) and as always this downtime has led to some reflection on my part. What I am about to write about, might just be me, but I found this useful and stress-relieving. maybe you do to.</p>

<p>We ended up in a cabin out in the archipelago that had spotty network access, only landline TV and our kids were at camps or with friends. Yes, we went directly into retirement. We set the clock to remind us about the TV-show that started 21.30.</p>

<p><strong>[UPDATE]</strong>
I totally forgot but a technical glitch happened that took away data traffic from my phone for about 4 days. That … reset … was very blissful and totally helped me make this transition and look at things differently.</p>

<p>What I noticed was how twitchy my fingers got, after just a few hours. I kept on picking up the phone, trying to check news or LinkedIn. When I went walking the dog, I kept trying to get some pod or music playing. Even at the beach reading a book, I also found myself trying to add some background music. When watching the show I desperately tried to also show something on YouTube or play a game etc.</p>

<p>It didn’t take me too long to just stop in my track and just go “What am I doing? Why all of these inputs?”.</p>

<!-- excerpt-end -->

<p>There’s nothing wrong with pods and I’ve learned so much from YouTube that I really should send them part of my monthly salary - but it’s so distracting. And I don’t know where the need of adding more sources comes from.</p>

<p>In fact, I’ve come so far that I thinking “chilling in the tv-sofa” as having the TV going, while I’m playing a game on the phone, talking to Elin and reading the news at the same time. Come on!</p>

<p>What I started to do was nothing revolutionary, but I very consciously started to remove inputs, lowering the number of sources to a reasonable amount.</p>

<p>Because, at least for me, my brain never get time to think when I’m constantly feeding it things to keep its attention on. And when you think about it, in most activity there are more than one source by definition:</p>

<ul>
  <li>commuting to work - I ride a bike and keep my attention there; seeing, hearing and pedaling. I don’t also need to know about US politics, sing along to a song or learn about why these particular laws in the Old Testaments was included. Just riding the bike it enough.</li>
  <li>walking (the dog) - just walking and keeping my attention on the dog is plenty. I don’t need to listen to an audio book too.</li>
  <li>watching movie - seeing a movie is plenty input already. I don’t need to look things up on IMDB too.</li>
  <li>coding or writing - doesn’t have to be accompanied by sound. The thoughts in my head on what to write and how to structure the text is enough.</li>
  <li>practicing my instrument - I don’t need to watch fun clips on YouTube while warming up. Or keep my phone handy on the stand, if the kids call. I will hear the signal.</li>
  <li>playing games on the phone - yes - I of course do that, but then I do not listen to pods or audio books too.</li>
</ul>

<p>Because your internal thoughts is an input to, and for years now I’ve not giving it room but rather blocking it out with other input sources.</p>

<p>Here’s my new routine; for every activity I’m doing, just before I start, I think about the inputs that are flourishing around me and I try to remove one. Not more to start with.</p>

<p>It took me about a week to stop picking up another input source. But now I’m almost longing to get one out. As I’m writing this I’m in Noise Cancelling head phones, with no sound on - just to block out some background music at the office.</p>]]></content><author><name>Marcus Hammarberg</name></author><category term="Lean" /><category term="Marcus private" /><summary type="html"><![CDATA[Just back from a four-week vacation (out of the 5 weeks mandated by law in Sweden, btw. Sweden is great!) and as always this downtime has led to some reflection on my part. What I am about to write about, might just be me, but I found this useful and stress-relieving. maybe you do to. We ended up in a cabin out in the archipelago that had spotty network access, only landline TV and our kids were at camps or with friends. Yes, we went directly into retirement. We set the clock to remind us about the TV-show that started 21.30. [UPDATE] I totally forgot but a technical glitch happened that took away data traffic from my phone for about 4 days. That … reset … was very blissful and totally helped me make this transition and look at things differently. What I noticed was how twitchy my fingers got, after just a few hours. I kept on picking up the phone, trying to check news or LinkedIn. When I went walking the dog, I kept trying to get some pod or music playing. Even at the beach reading a book, I also found myself trying to add some background music. When watching the show I desperately tried to also show something on YouTube or play a game etc. It didn’t take me too long to just stop in my track and just go “What am I doing? Why all of these inputs?”.]]></summary></entry></feed>