<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Coding with Jesse</title>
        <link>https://codingwithjesse.com/</link>
        <description>Svelte, JavaScript, CSS, HTML, and anything else of interest to standards-loving web designers and developers</description>
        <lastBuildDate>Mon, 02 Mar 2026 01:57:48 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Coding with Jesse</title>
            <url>https://www.codingwithjesse.com/images/me.jpg</url>
            <link>https://codingwithjesse.com/</link>
        </image>
        <copyright>Copyright 2026, Jesse Skinner</copyright>
        <item>
            <title><![CDATA[A rabbit hole in 5 commits]]></title>
            <link>https://www.codingwithjesse.com/blog/a-rabbit-hole-in-5-commits/</link>
            <guid>https://www.codingwithjesse.com/blog/a-rabbit-hole-in-5-commits/</guid>
            <pubDate>Mon, 02 Mar 2026 01:57:48 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2026-03-01-rabbit-hole.jpg" alt="looking up out of a hole" width="800" height="530"/>
<cite>Photo by <a href="https://unsplash.com/@xerty?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Mr Xerty</a> on <a href="https://unsplash.com/photos/a-circular-hole-in-the-ground-with-trees-growing-out-of-it-zBuX7W0rBSk?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></cite>

<p>When I&#39;m working with a client, I respect their time and energy by keeping things simple and easy for them.
I make as many decisions as I can, and try not to bog them down with every complex decision I have to make.</p>
<p>I&#39;ve learned to trust my instincts, and to escalate important choices only when the trade-offs are truly business decisions.</p>
<p>This is a story of when this instinct led me in the wrong direction.</p>
<h2 id="a-static-link-to-a-static-zip-file">A static link to a static zip file</h2>
<p>I often work on projects with fixed-price and flexible-scope.
What this means is my clients know exactly what a project will cost, but I&#39;m also okay with the odd surprise.</p>
<p>That day, I was working on a web app that was powered by static data files.
I was working on the simplest task of the project.
The client wanted to add a link so users can download all the data files in a zip file.
Sounds straightforward, right?</p>
<h2 id="commit-1-new-add-data-download">Commit #1: &quot;NEW: add data download&quot;</h2>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>data.zip<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Download Data<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre>
<p>I could have just zipped the data files up on my laptop and called it a day.
However, I didn&#39;t want to be in a situation where I forgot to update the zip file if the data changed.
I decided to automate the process by creating the zip file during the automated deployment pipeline.</p>
<h2 id="commit-2-new-create-zip-file-in-deployment-pipeline">Commit #2: &quot;NEW: create zip file in deployment pipeline&quot;</h2>
<pre><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"npm run build:download; ..."</span><span class="token punctuation">,</span>
<span class="token property">"build:download"</span><span class="token operator">:</span> <span class="token string">"cd public/static/data; rm -f data.zip; zip *.csv data.zip"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<p>I pushed that change and the pipeline failed immediately.</p>
<pre><code>sh: 1: zip: not found</code></pre><p>Seriously?
I looked at the pipeline configuration in <code>bitbucket-pipelines.yml</code>.
It was running off a seven-year-old container that didn&#39;t have zip installed.</p>
<p>I could have made a new, custom container that had everything we needed.
I decided that would give the project another thing to manage over the long run, which I thought it&#39;d be best to avoid.
Instead, I thought I&#39;d simplify things by using the official node.js container.</p>
<p>I added a line to the pipeline to install rsync (to upload files to the staging server) and zip.
It would only add a few seconds to the pipeline, but then we wouldn&#39;t need to maintain a custom container.</p>
<h2 id="commit-3-fix-use-latest-nodejs-container-install-rsync-and-zip">Commit #3: &quot;FIX: use latest node.js container, install rsync and zip&quot;</h2>
<pre><code class="language-yaml"><span class="token key atrule">image</span><span class="token punctuation">:</span> node<span class="token punctuation">:</span>latest
<span class="token key atrule">pipelines</span><span class="token punctuation">:</span>
  <span class="token key atrule">branches</span><span class="token punctuation">:</span>
    <span class="token key atrule">main</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token key atrule">step</span><span class="token punctuation">:</span>
          <span class="token key atrule">script</span><span class="token punctuation">:</span>
            <span class="token punctuation">-</span> apt<span class="token punctuation">-</span>get update
            <span class="token punctuation">-</span> apt<span class="token punctuation">-</span>get install <span class="token punctuation">-</span>y rsync zip</code></pre>
<p>The pipeline failed again.</p>
<pre><code>&gt; npm run build:download; webpack --mode=production --env.router hash --env.appPublicPath public --config ./production.config.js

Error in bail mode: Error: callback(): The callback was already called.
    at context.callback (/opt/atlassian/pipelines/agent/build/node_modules/loader-runner/lib/LoaderRunner.js:106:10)
    at processTicksAndRejections (node:internal/process/task_queues:103:5) Error: callback(): The callback was already called.
    at context.callback (/opt/atlassian/pipelines/agent/build/node_modules/loader-runner/lib/LoaderRunner.js:106:10)
    at processTicksAndRejections (node:internal/process/task_queues:103:5)</code></pre><p>What the heck?</p>
<p>The web app was being built with Webpack v4, a very old build tool.
Turns out Webpack v4 breaks on newer versions of node.js.</p>
<p>I decided to try using an older node.js container.
After some experimentation, I found that Webpack v4 needed node.js v16 or older.
Of course, once I had the right version of node, the build in the container still failed!</p>
<pre><code>E: The repository &#39;http://deb.debian.org/debian buster Release&#39; does not have a Release file.
E: The repository &#39;http://deb.debian.org/debian-security buster/updates Release&#39; does not have a Release file.
E: The repository &#39;http://deb.debian.org/debian buster-updates Release&#39; does not have a Release file.</code></pre><p>This time it was because this old node docker container is built on an old and outdated version of Linux.
The operating system is so old that I couldn&#39;t even install rsync and zip on it without some serious workarounds.</p>
<h2 id="if-you-come-to-a-fork-in-the-road-take-it">If you come to a fork in the road, take it</h2>
<p>At this point I knew I was going in the wrong direction.
Getting old versions to work together wasn&#39;t the simple &amp; quick solution I had hoped it was.
I wasn&#39;t going to spend any more time getting five-year-old software to play nice together.
The best solution was to modernize this legacy project by replacing Webpack with Vite.</p>
<p>My client had surely never heard of Webpack or Vite, and they certainly would never have asked me to make the switch.
I knew it was way outside the scope of this project.
I also knew it would take me way longer than I estimated.</p>
<p>But more than all of that, I knew it was the right solution for this moment, and for the project in the long term.
If I didn&#39;t deal with this properly today, the system would remain fragile, and only get worse.
More problems like these would surely arise in the future, and I didn&#39;t want that burden and risk to persist.</p>
<p>I decided to push forward.</p>
<h2 id="commit-4-fix-replace-webpack-with-vite">Commit #4: &quot;FIX: replace Webpack with Vite&quot;</h2>
<pre><code>Lines updated +2778 -8272</code></pre><p>This was the deepest part of the rabbit hole, but I was happy to finally be rid of Webpack.
The project was much better off for it, no longer stuck on outdated versions and tools.
I was bracing myself for a huge task, but it didn&#39;t take as long as I feared, and I was very happy with the result.</p>
<p>Finally, I was able to use the latest versions of everything in the deployment pipeline, and it worked!</p>
<h2 id="delivering-the-download-button">Delivering the download button</h2>
<p>After all this, I sent my client an email and let them know the download button was in place.
I explained that the zip file was being built automatically in the deployment pipeline.
Best of all, I had modernized the pipeline so it was running the latest versions of everything and would even be a bit faster.</p>
<p>The client thanked me and asked, will the zip file update automatically when they upload new data files?</p>
<p>Damn, of course not.
The deployment pipeline is used during development only, when I push changes to the staging server.
Once the web application was live, I had assumed the client would create zip files manually whenever they uploaded new data files.
It was a bad assumption on my end, and of course I was happy to deliver a better solution that works well for my client.</p>
<h2 id="commit-5-new-use-jszip-to-generate-the-zip-file-in-the-browser">Commit #5: &quot;NEW: use jszip to generate the zip file in the browser&quot;</h2>
<pre><code class="language-javascript"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">download</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// import jszip only when needed</span>
  <span class="token keyword">const</span> JSZip <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'jszip'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>default<span class="token punctuation">;</span>
  <span class="token keyword">const</span> zip <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JSZip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> filename <span class="token keyword">of</span> <span class="token constant">CSV_FILES</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    zip<span class="token punctuation">.</span><span class="token function">file</span><span class="token punctuation">(</span>filename<span class="token punctuation">,</span> <span class="token keyword">await</span> <span class="token function">fetchFile</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">const</span> zipBlob <span class="token operator">=</span> <span class="token keyword">await</span> zip<span class="token punctuation">.</span><span class="token function">generateAsync</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'blob'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token constant">URL</span><span class="token punctuation">.</span><span class="token function">createObjectURL</span><span class="token punctuation">(</span>zipBlob<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> link <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'a'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  link<span class="token punctuation">.</span>href <span class="token operator">=</span> url<span class="token punctuation">;</span>
  link<span class="token punctuation">.</span>download <span class="token operator">=</span> <span class="token string">'data.zip'</span><span class="token punctuation">;</span>
  document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>link<span class="token punctuation">)</span><span class="token punctuation">;</span>
  link<span class="token punctuation">.</span><span class="token function">click</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>link<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token constant">URL</span><span class="token punctuation">.</span><span class="token function">revokeObjectURL</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I deleted all the zip stuff from the pipeline, but of course I kept the modernization.
I switched to creating the zip file on the fly in the browser using the <code>jszip</code> library.
The download button was working great.</p>
<h2 id="simplifying-things-for-my-clients">Simplifying things for my clients</h2>
<p>When given a problem, I&#39;m always looking to deliver the simplest solution.
I&#39;m willing to do the hard work to keep things simple for my clients, and for the users of the software.</p>
<p>At the start, having a static zip file seemed like the simplest solution.
In the end, the best solution was the one that worked best for my client, and I was happy to deliver that instead.</p>
<p>By keeping things simple for my clients, I took on a lot of complexity.
I went further down this rabbit hole than I had to, but I&#39;m glad this legacy web application ended up being modernized in the process.</p>
<p>This easy task ended up taking way longer than I had expected, but my client never needed to know that.
My client was very happy with the result.
From their perspective, their project had been modernized, but I still finished everything on budget and on time.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/2026-03-01-rabbit-hole.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Unpivoting]]></title>
            <link>https://www.codingwithjesse.com/blog/unpivoting/</link>
            <guid>https://www.codingwithjesse.com/blog/unpivoting/</guid>
            <pubDate>Mon, 16 Feb 2026 15:02:21 GMT</pubDate>
            <content:encoded><![CDATA[<p>In 1989, when I was eight, I started learning Logo and Basic programming on my Coleco Adam.
I was absolutely fascinated to realize those video games like Buck Rogers were made with the same programming tools I was learning!
I wanted to learn how to make computers do things like that.</p>
<p>In 1998, when I was seventeen, I had my first job at a movie theatre.
On my break, I would fantasize about starting a web design company.
I wondered if I could charge $10 per page.
Maybe that was too much to ask?</p>
<p>In 2001, when I was twenty, I got my first real programming summer job while attending the University of Waterloo.
I learned ColdFusion and web development.
I got to design and build an entire Content Management System.
I even spoke at a couple conferences and was published in ColdFusion Developers Journal.
I loved this world and knew that this was exactly where I wanted to be.</p>
<h2 id="the-first-pivot">The first pivot</h2>
<p>As much as I loved coding, I saw my bosses spend their time in meetings and writing emails, and I felt like they were superior to me somehow.
In 2005, at twenty-four, I moved to Berlin, <a href="https://www.codingwithjesse.com/blog/first-post/">started this blog</a>, and bought Consulting for Dummies.
I wondered if maybe consulting work would be a step up the success ladder.
I started freelancing, and everyone only wanted to pay me for coding.
Since that&#39;s what I loved to do most, I didn&#39;t complain.
I unpivoted, and thought I&#39;d revisit the consulting idea another time.</p>
<h2 id="the-second-pivot">The second pivot</h2>
<p>My freelancing business grew, and I had to say &quot;no&quot; to a lot of people.
Then I read &quot;The E-Myth&quot; and Peter Drucker, and decided my company needed to grow.
At some point I had a dozen developers working for me, and even hired a Project Manager or two.
I was able to say &quot;yes&quot; to everyone.
I had pivoted to growing an agency, moving closer to this idea of success I kept reading about.</p>
<p>Then it stopped being fun.
I would start off chatting with a new client, but turn it over to a PM when it started to get interesting.
I was juggling so many projects in my mind, I barely had time or energy to do any coding.
It felt like driving a car from the backseat.</p>
<p>One time, I had a developer stop answering emails, and had to jump in to save the project.
I read through weeks of emails back and forth between the frustrated client, the project manager and the overwhelmed developer.
The project became clear in my mind and I quickly built exactly what the client wanted in a weekend.
I was back in the driver&#39;s seat, working directly with my client to build a web app.</p>
<p>I decided to unpivot.
I stopped hiring.
I sent my developers off to work with some of my clients directly.
I wound it all down until it was just me building for a few good clients.
I was back on the path again.</p>
<h2 id="the-third-pivot">The third pivot</h2>
<p>I&#39;d always wanted to be a teacher.
I was thrilled when my friend invited me to teach a programming course at Georgian College.
I poured myself into teaching and loved everything about it.
Even though I was teaching part time, all the prep work and marking really cut into my freelance work, and my income.
At the end of the term, I asked my wife to remind me that I should probably say &quot;no&quot; next time.</p>
<p>Of course, I didn&#39;t say &quot;no&quot;, because I was thrilled all over again the next time I was asked.
And the next time.
And the next time.
I taught a half dozen classes over the next years, and absolutely loved helping students.
I loved thinking about how to explain programming concepts to my students.</p>
<p>But I was spending so much time creating assignments and tests, and marking the students just to give them an arbitrary grade.
I loved teaching, but I was never passionate about the college system.
I decided it wasn&#39;t worth it anymore and unpivoted again.</p>
<h2 id="the-fourth-pivot">The fourth pivot</h2>
<p>The teaching itch never left me, so I decided to start teaching online.
That way I could help students directly without all the tests and marking.
I started making YouTube videos, streaming on Twitch, and even developed and launched a video course, <a href="https://www.codingwithjesse.com/blog/lessons-learned-from-my-first-video-course/">The Joy of Svelte</a>.
I started a newsletter and imagined this as the start of a series of courses.</p>
<p>Making a course took a lot more time and energy than I expected.
It was also missing something from teaching students directly in the classroom.
I was <a href="https://www.codingwithjesse.com/blog/finding-time-for-side-projects/">procrastinating my course development</a> and spending all my time coding for clients.
I felt bad about this, like I was stuck in a rut.
Eventually, I realized I wasn&#39;t stuck at all.
I was doing the thing I loved the most, helping people directly.</p>
<p>I stopped feeling bad about it, and let go of pressuring myself to be a course creator.
I had unpivoted back to focusing on the thing I love the most, working directly with clients to build web apps.</p>
<h2 id="the-fifth-pivot">The fifth pivot</h2>
<p>I was watching YouTube videos and listening to podcasts about business.
They talked about &quot;hands-on&quot; work as being undesirable.
These motivational speakers made me feel bad for wasting my limited time coding when I could be helping more people with my knowledge and experience.
They convinced me the only way forward would be to pivot away from coding and to focus on being a coach &amp; consultant.</p>
<p>I put on a blazer, had business cards made, and attended a tech conference.
I told everyone I was like a &quot;personal trainer for developers&quot;.
I had this vision of using my experience and teaching skills to help development teams. </p>
<p>Every time I worked with a team to coach them on how to build or fix things, I ended up just building prototypes for them.
I found it way easier, and frankly more fun, to build things instead of just telling people how to do it themselves.</p>
<p>I realized the best way to help people was the way I always had, by building things for them directly.</p>
<h2 id="learning-by-experimenting">Learning by experimenting</h2>
<p>I learned that I crave challenge and novelty.
With each pivot, I <a href="https://www.codingwithjesse.com/blog/follow-the-light/">followed my curiosity</a>, or my insecurities, and tried out something new.
Even when it didn&#39;t work, I learned something new about myself.</p>
<p>Each time, I&#39;m glad I only experimented by dipping a toe in the water and never dove in completely.
I&#39;m grateful for those clients that stuck with me from the beginning, some of whom I&#39;m still working with to this day.</p>
<p>Having gone through this cycle so many times, it&#39;s now comically clear that I&#39;ve always done exactly what I should be doing.
I&#39;m a Web Developer, I build web apps for my clients, and I&#39;ve been doing it non-stop since 2001.</p>
<p>I&#39;ve stopped looking for greener pastures, and can now see how wonderful things are right now.
I don&#39;t need anything different.
I get to do even more of what I love, what I&#39;ve always loved.</p>
<p>At least until the next pivot comes along.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/me.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Chrome broke my client's web app]]></title>
            <link>https://www.codingwithjesse.com/blog/chrome-broke-my-clients-web-app/</link>
            <guid>https://www.codingwithjesse.com/blog/chrome-broke-my-clients-web-app/</guid>
            <pubDate>Sat, 14 Feb 2026 16:46:34 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2026-02-14-chrome-broke.jpg" width="800" height="450" alt="Google Chrome icons"/>
<cite>Photo by <a href="https://unsplash.com/@growtika?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Growtika</a> on <a href="https://unsplash.com/photos/diagram-dUMEnARXgJU?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></cite>

<p>The great thing about the web is that web pages from thirty years ago still work fine.
So you can imagine how surprised I was when a new version of Chrome broke my client&#39;s web app in production.</p>
<h2 id="business-as-usual">Business as usual</h2>
<p>The web app is a data visualization tool with a colourful d3 SVG map.
Hovering over a country brings up a tooltip, and clicking a country brings up a larger panel with data.
Although the application was originally developed in 2013, I took it over in 2022.</p>
<p>I was working on some changes to the web app a couple of weeks ago.
I run Linux (<a href="https://knowyourmeme.com/memes/btw-i-use-arch">Arch btw</a>) so I always have the latest version of everything, including Chrome.
I spun up the development site one day and suddenly the map tooltips and clicking stopped working.</p>
<p>Did I install something new that broke the interactions?
Maybe one of my Chrome extensions was messing with something?
I tried rebooting, as one does when they&#39;re out of ideas.
Still didn&#39;t work.</p>
<p>I loaded up Firefox.
Everything worked fine.
I grabbed my mac and tested it out in Chrome.
Everything worked fine there too.
I sent off my changes for the day for my client to review, and they didn&#39;t say anything about it being broken.</p>
<p>I could have left it at that, but it didn&#39;t sit right with me.
That evening I started digging into it more.</p>
<h2 id="classically-debugging-a-hard-bug">Classically debugging a hard bug</h2>
<p>First thing I did was add <code>console.log</code> statements to the d3 event handlers.</p>
<p>I could see that the <code>mouseenter</code> and <code>mouseleave</code> events were working, but the <code>mousemove</code> and <code>click</code> handlers never fired.
I tried remove the <code>mouseenter</code> and <code>mouseleave</code> handlers, and suddenly the <code>mousemove</code> and <code>click</code> started working again.</p>
<p>I put back the <code>mouseenter</code> and <code>mouseleave</code> and looked for something that could have been responsible.
Then I found this suspicious line:</p>
<pre><code class="language-javascript"><span class="token comment">// bring it to the front and maintain full stroke</span>
path<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>I removed it and it started working again!
But that line was written six years ago, before I ever worked on the app.
Why would that be to blame?
What changed?</p>
<h2 id="it-is-never-a-compiler-bug">It Is Never a Compiler Bug</h2>
<p>There&#39;s a trope in programming that &quot;it&#39;s never a compiler bug&quot;.
As a web developer, we would say &quot;it&#39;s never a browser bug&quot;.
Well could it be this time?</p>
<p>I was running Chrome v144 so I found the <a href="https://developer.chrome.com/release-notes/144">release notes</a>.
It came out on January 13th, so the timing made sense.</p>
<p>I scrolled and skimmed and finally found something that sounded like it might be relevant:</p>
<blockquote>
<h3 id="interoperable-pointer-and-mouse-boundary-events-after-dom-changes">Interoperable pointer and mouse boundary events after DOM changes</h3>
<p>After an event target is removed from the DOM, the logical target of the pointer, as implied by the Pointer and Mouse boundary events (that is, over, out, enter, and leave events), should be the nearest ancestor still attached to the DOM.</p>
</blockquote>
<p>This led me to find a bug filed for Chromium v144, <a href="https://issues.chromium.org/issues/477545529">Regression in Chrome 144: Moving event target in &quot;mouseenter&quot; handler breaks click+mouseleave events</a>.
It <em>was</em> a Chrome bug!
The example code even matched mine almost exactly:</p>
<pre><code class="language-javascript">redSquare<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>redSquare<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>It seems like this is a permanent change due to a refinement of the spec.
It&#39;s apparently a feature-not-a-bug, called <code>BoundaryEventDispatchTracksNodeRemoval</code>, so it&#39;s probably never going to be fixed.</p>
<p>There&#39;s fortunately a suggestion in a comment of how to fix this on my end though:</p>
<pre><code class="language-javascript"><span class="token keyword">if</span> <span class="token punctuation">(</span>elem<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span>lastChild <span class="token operator">!==</span> elem<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    elem<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>elem<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Makes total sense!
I made the change, and everything was working just as it had been a couple of weeks earlier!</p>
<h2 id="always-trust-your-gut">Always trust your gut</h2>
<p>I&#39;m going to resist blaming the browser next time I have a bug, but it&#39;s very interesting to be reminded that it can and does happen.</p>
<p>The important takeaway is that when I see something wrong on a client project, I&#39;m not going to be able to rest until I figure out exactly why.
I needed to be sure it was limited to my computer, because if it wasn&#39;t, the last thing I&#39;d want is for my client to find out from one of their users.</p>
<p>I of course pushed the change that night and let my client know what had happened, and that the production site was already being affected.
Fortunately, v144 hadn&#39;t been fully rolled out.
Even as I write this, less than 4% of users are running v144.
This also underscores the importance of developers testing using the newest versions.</p>
<p>As a developer, I always want to fix bugs before my client notices, and definitely before their users notice.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/2026-02-14-chrome-broke.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Coding with LLMs can still be fun]]></title>
            <link>https://www.codingwithjesse.com/blog/coding-with-llms-can-still-be-fun/</link>
            <guid>https://www.codingwithjesse.com/blog/coding-with-llms-can-still-be-fun/</guid>
            <pubDate>Sat, 17 Jan 2026 19:39:05 GMT</pubDate>
            <content:encoded><![CDATA[<p>Do you love reviewing AI-generated code?
Do you get a tickle of pure joy to find and criticize the mistakes and problems in hallucinatory slop?
Me neither.</p>
<p>You know what I do love?
I love pouring my creativity and insight and empathy into a project.
I love designing architectures and solutions that actually make things better for users.
I love getting in the flow state, cranking away at a problem, building brick upon brick until the creation comes to life.</p>
<p>If you&#39;re not careful, AI tools will have you spending all your time doing code review.
It&#39;s very hard to get into a flow state when you&#39;re waiting on your agents and reviewing what they do.
Fortunately, I&#39;ve found a way to use LLMs to do a <em>lot</em> more of what I love.</p>
<h1 id="curiosity-and-excitement">Curiosity and excitement</h1>
<p>Since I was a child, I wondered how it was possible that humans were able to get a computer to do so many things.
It felt like magic, and I wanted to be that magician.
Forty years later, that feeling still guides me through my software development career.</p>
<p>While LLMs stir that curiosity and excitement, they also threaten to take it away.
If, like me, you have very <a href="https://www.codingwithjesse.com/blog/how-i-feel-about-ai/">mixed feelings</a> about it all, you might be both excited and worried about the potential.</p>
<p>I&#39;ve found LLMs to be very helpful ever since Copilot came out in 2021, and they have definitely <a href="https://www.codingwithjesse.com/blog/how-i-use-github-copilot-to-be-more-productive/">made me more productive</a>.
<a href="https://www.codingwithjesse.com/blog/coding-with-chatgpt/">My views on coding with LLMs</a> haven&#39;t changed much along the way.
Five years later, I wanted to share with you how I&#39;m using LLMs today, and how they&#39;re making my job more fun than ever.</p>
<h1 id="a-workflow-that-lets-you-work-in-flow">A workflow that lets you work in flow</h1>
<p>The secret is to find a workflow that works for you, that keeps you engaged and in a state of flow.
I&#39;ve found the following workflow that works really well for me.
I use it every day for almost every multi-step coding task I work on.
It basically starts with the following context:</p>
<pre><code>When the user gives you a task specification:

1. Explore the codebase to find relevant files and patterns
2. Break the task into a small number of steps. Each step should include:
    a. a brief, high-level summary of the step
    b. a list of specific, relevant files
    c. quotes from the specification to be specific about what each step is for
3. Present the steps and get out of the way.

When the user says &quot;done&quot;, &quot;how&#39;s this&quot;, etc.:

1. Run git status and git diff to see what they changed
2. Review the changes and identify any potential problems
3. Compare changes against the steps and identify which steps are complete
4. Present a revised set of steps and get out of the user&#39;s way.

Important:
- Be concise and direct, don&#39;t give the user a lot to read
- Allow the user to make all technical, architectural and engineering decisions
- Present possible solutions but don&#39;t make any assumptions
- Don&#39;t write code - just guide
- Be specific about files and line numbers
- Trust them to figure it out</code></pre><p>You can paste this in a new chat, or set up a &quot;custom agent&quot; or &quot;skill&quot; if you want to be fancy.
I use a Claude Code skill for this, but it&#39;ll work with any LLM coding tools.
Ideally it&#39;ll have access to your codebase and can work as a search engine to point you in the right place.
I find this really speeds me up, especially on new codebases, or code I haven&#39;t touched in a long time.</p>
<p>With this workflow, I&#39;m not spending hardly any time waiting on the LLM to generate content.
I find this approach uses the LLM for what it does best.
Hallucinations are almost non-existent, because everything it needs is available in the present context.
There&#39;s very little sycophancy, and not much back-and-forth chatting at all.</p>
<p>I specifically don&#39;t want to be reading through pages of markdown.
If it makes a mistake at a high-level, I can easily spot and correct it, or just choose to do something different.</p>
<p>Often I&#39;ll still decide to ask the LLM to write code for me, but I keep it limited to a small step in this process at a time.
I sometimes have it scaffold out some empty modules for me while I work on a different step.
Often the steps are very simple or mechanical, so it&#39;s actually easier to have the LLM complete it for me.
I&#39;ll use it where LLM-generated code can speed me up, where I&#39;m not wasting time babysitting or directing it to do better.
I&#39;m in full control of choosing which parts I want to work on, and which I don&#39;t.
Either way, I&#39;m staying fully engaged, and I know what&#39;s happening at any given moment.
It&#39;s like wearing a jetpack that I&#39;m steering, rather than a team of minions that I have to manage.</p>
<p>This workflow is easy to modify.
You can change it to suit your preferences, and add rules that make it work better for you.
You can do less of the coding if you want, or even use it as a planning step in your vibe coding.</p>
<p>It&#39;s also dead simple to understand, with nothing special you have to learn.
You&#39;re not messing around with prompt engineering.
There are no MCP servers to install, no special plugins.
It works with all models, even cheap or local models.
You don&#39;t have to keep up with the latest techbro videos to make a workflow that works for you.</p>
<h2 id="find-your-own-workflow">Find your own workflow</h2>
<p>I invite you to find a workflow that empowers you to do your best work while staying out of your way.
Stay more engaged, lose track of time, reduce friction, solve problems and do your best work.</p>
<p>Please share what works for you, and if you have any suggestions to make coding with LLMs even more fun.
I hope we can all learn from and inspire each other to make coding more fun than ever.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/me.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Weekly Cadence]]></title>
            <link>https://www.codingwithjesse.com/blog/weekly-cadence/</link>
            <guid>https://www.codingwithjesse.com/blog/weekly-cadence/</guid>
            <pubDate>Thu, 04 Sep 2025 19:06:45 GMT</pubDate>
            <content:encoded><![CDATA[<p>I have a weekly cadence with my clients. This means they expect me to make progress every week. But there&#39;s no expectations for any particular day.</p>
<p>I&#39;m also careful not to over-commit. I leave myself room to breathe. If I decide to go beyond that and work extra, everyone&#39;s happy. If I don&#39;t, that&#39;s okay too.</p>
<p>My day is a blank canvas. I get to decide how I&#39;ll fill it. What am I most curious or excited about? What do I want to work on, if anything?</p>
<p>Being able to set your own hours as a freelancer doesn&#39;t mean choosing 8-4 vs 10-6. It&#39;s being free to take a day off to play with your kids or finish an exciting book. It means being able to live the cozy life you need for a healthy body and mind.</p>
<p>Ultimately it means my work is higher quality because I&#39;m not rushing, I&#39;m well rested, and I love the work I do.</p>
<p>A weekly cadence won&#39;t work for all clients, nor for all freelancers, but it suits me and my clients perfectly.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/me.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Back to School & Back to Work]]></title>
            <link>https://www.codingwithjesse.com/blog/back-to-school-back-to-work/</link>
            <guid>https://www.codingwithjesse.com/blog/back-to-school-back-to-work/</guid>
            <pubDate>Tue, 02 Sep 2025 17:04:27 GMT</pubDate>
            <content:encoded><![CDATA[<p>The past few months, for the first time, I prioritized coziness &amp; rest, minimizing stress and ambition and mindfully enjoying travelling with my family.</p>
<p>I just dropped my son off at school, and now I feel it&#39;s time to start a new season of focusing on my business again.</p>
<p>But I feel the need to bring that coziness back into my professional life and find ways to do bigger things while still being restful and relaxed.</p>
<p>It&#39;ll be a big challenge, but it&#39;s very important to me to get this right.</p>
<p>I don&#39;t want to hustle. I&#39;m not desperate to be more successful. What I desire is more creativity, more freedom, more fun, following my curiosity and joy and excitement through my work. I want to help more people, particularly freelancers like myself. I want to go beyond my client work and work on projects for myself.</p>
<p>In the past it&#39;s been hard to juggle that stuff with my need to trade hours for money. That&#39;s where the challenge comes in, to do more but to keep it fun and cozy and stress free.</p>
<p>I think I&#39;ve come to see how lowering expectations for myself really helps to take the pressure off. I&#39;m hoping to thread that needle of doing more in some areas, maybe doing less in others to compensate, and being very mindful and measured in making sure I&#39;m able to handle it all gracefully.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/me.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Living a Cozy Life]]></title>
            <link>https://www.codingwithjesse.com/blog/living-a-cozy-life/</link>
            <guid>https://www.codingwithjesse.com/blog/living-a-cozy-life/</guid>
            <pubDate>Sun, 13 Jul 2025 10:08:39 GMT</pubDate>
            <content:encoded><![CDATA[<p>Six months ago, my wife was about to give birth to our second child.
Shortly after our baby was born, I took on a really big client project with a firm deadline.
I also signed up for a Creative Writing class, not sure if I&#39;d be able to juggle it all.</p>
<p>I barely pulled it all off thanks to my wife&#39;s support.
When the project was delivered and the course ended, I desperately desired something different.</p>
<p>I desired rest, comfort and relaxation.
I desired simplicity.
I desired peace and quiet.
I desired being fully present with my family.</p>
<p>What I really desired was a different quality of life, one that I&#39;d only experience on holiday.
I wanted to wake up with a clear and quiet mind, to hear the birds chirping and the breeze in the trees instead of worried thoughts.
I wanted to move slowly, with all the time in the world.</p>
<p>I&#39;ve always set ambitious goals for myself.
I feel like I&#39;ve been going as fast as I could for years.
For the first time, I had a different goal: <em>to live a cozy life</em>.</p>
<p>Sure, I had major constraints: we had a new baby, an older son, and I still needed to do freelance work.</p>
<p>I didn&#39;t have to work on any side projects, though.
I could let my blog and newsletter go quiet for a while.
I could say no to filling up my calendar.
I could stop making To Do lists and allow myself to do a lot less.</p>
<p>I had to remind myself that the bare minimum is, by definition, good enough.
As long as my family was taken care of, as long as I was making enough money to cover our expenses, I was doing okay.</p>
<p>Within days I was daydreaming about new big projects I could start.
I had ideas for courses I wanted to launch, books I wanted to write.
I had to learn to say &quot;no&quot; to myself too.</p>
<p>After a week or two, I started to get really bored for the first time in a long time.
I decided to limit myself to low-stakes activities, things that didn&#39;t matter if I did them or not.
I was happy to read books, especially books that I started long ago but never finished.
I enjoyed playing video games, and I even finished the original NES Final Fantasy I&#39;d started 30 years ago!</p>
<p>I started getting excited about programming again, and I found myself watching videos and reading about web development.
I was enjoying work more too, because I wasn&#39;t stressed out or in a rush, and I was able to enjoy the interesting challenges.</p>
<p>Every day that went by, I felt myself recharging.
It felt like I was unlocking new levels of rest, new mental states I hadn&#39;t had access to in a long time.
The less that I did, the more I could do.</p>
<p>I had the cozy life I wished for, and I finally had room to breathe.</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/me.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[From Full-Time to Freelance: How I made the leap]]></title>
            <link>https://www.codingwithjesse.com/blog/from-full-time-to-freelance/</link>
            <guid>https://www.codingwithjesse.com/blog/from-full-time-to-freelance/</guid>
            <pubDate>Tue, 14 Jan 2025 17:24:45 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2025-01-14-full-time-to-freelance.jpg" width="800" height="400" alt="Laptop and a coffee"/>

<p>I was already dreaming of starting my own business before I ever got my first job.
My very first job was at a movie theatre in the late twentieth century.
In my breaks I would brainstorm ideas for starting a web design business.
I would fantasize about proposing to redesign the movie theatre&#39;s website.
When the boss wasn&#39;t looking, I would sketch out my web site and my pricing on scraps of paper.</p>
<p>Later, I got my first programming job, working on a content management system.
I learned a lot about business at that job.
The company would always be bringing in new clients.
Even then, I would fantasize about being able to have my own clients one day.
I would read books about self-employment and consulting.
I&#39;d read anti-work websites and dream of getting out of the rat race.</p>
<p>A few years later, I met my future wife and moved to Berlin, Germany to live with her.
I had the best excuse for quitting my job back home, and saw it as a chance to start my own business.
Almost exactly twenty years ago, I registered a business name, &quot;The Future of the Web&quot;, bought thefutureoftheweb.com, and started blogging about web development.</p>
<p>Unfortunately, I had only three months to find a job in Berlin before my tourist visa would expire.
I went to an Internet cafe every day and applied to 3+ job postings, hoping my Java experience would make up for my inability to speak German.
Extremely luckily, I was hired by Strato Rechenzentrum, a major Internet hosting company.
When I received my work visa, I was surprised to see that it had two conditions: I was approved to work at Strato, but I was also permitted to do freelance work (&quot;freiberuflich arbeiten&quot;).</p>
<p>While at Strato, I continued to fantasize about breaking free of employment and working on my own terms.
I would read every article I could about self-employment or freelancing.
I would listen to audiobooks and podcasts, and just surrounded myself with content that inspired and motivated me.</p>
<p>One day, I got up the courage to tell my manager about my work visa condition, and to ask whether Strato would allow me to do freelance work on evenings and weekends.
My manager saw no issue with it, and he verified this with HR.
I was given the green light to go ahead.</p>
<p>I had already been blogging about web development for a little while, so I updated my blog to make it clear I was available for hire.
I published <a href="https://www.codingwithjesse.com/blog/hire-an-ajax-freelancer/">&quot;Hire an Ajax Freelancer&quot;</a> to boldly announce that I was available to take on some JavaScript and Ajax work.</p>
<p>A month later, someone found me and asked me to do a small UI thing.
I charged $100/hour, worked for three hours, and billed the person $300.
He paid me on PayPal a few hours later.
It was the best feeling in the world!</p>
<p>I continued doing work for this person, and the next month someone else contacted me for a small job.
I was soon making $500-1000/month in my free time, on top of my full-time salary work at Strato.
It was tiring, but I was so highly motivated and inspired, I knew that I had to push through to make it to achieve my dream.</p>
<p>When my then-girlfriend finished school, she wanted to take six months off and live in my hometown in Canada.
I quit my job at Strato and announced I was <a href="https://www.codingwithjesse.com/blog/freelancing-fulltime/">Freelancing Fulltime</a>
This was the ultimate test: could I survive off of freelancing alone?
If it failed, I felt like I would be able to go and find another job, but I was hoping it wouldn&#39;t come to that.</p>
<p>I put the word out privately among friends, and my friend&#39;s uncle hired me for a much larger project.
I continued blogging regularly to try to promote myself by sharing my expertise.
I was fortunate to have people contact me for new projects every few weeks.
Two months in, I published <a href="https://www.codingwithjesse.com/blog/freelancing-first-two-months/">Freelancing: First two months</a>.
I was approved to speak about <a href="https://www.codingwithjesse.com/blog/unobtrustive-ajax-at-the-ajax-experience/">Unobtrusive Ajax at The Ajax Experience</a> conference in Boston, and got to meet my idols like John Resig and Douglas Crockford.
Eight months in, I published <a href="https://www.codingwithjesse.com/blog/freelancing-first-eight-months/">Freelancing: First eight months</a>.</p>
<p>It was a wild experience, and the momentum was growing.
It was in the following months that I found three clients in particular that I would continue working with for years.
Two of them are still my active clients that I&#39;m working for this very moment.</p>
<p>Do you also dream about leaving your job and starting on a similar path?
There are a lot of things you can do right now, while you&#39;re still working in a full-time job:</p>
<ol>
<li>Be clear about your goals, and visualise the life you want to live.</li>
<li>Surround yourself with content that inspires you and reminds you of moving towards your goals. These could be books, social media accounts, YouTube channels, podcasts, etc.</li>
<li>Start putting out content that demonstrates your knowledge and expertise. Share everything you learn. Share resources and tips. You could start a blog, a professional social media presence, a mailing list, a podcast, or whatever you feel inspired and motivated to do.</li>
<li>Start a business. Depending on where you live, you&#39;ll need to register a business name, have a lawyer give advice and help draft a standard client contract, learn about taxes from an accountant, and probably purchase extra business insurance.</li>
<li>Find out if your employer allows you to freelance on evenings and weekends.</li>
<li>If so, put out the word on social media, and put the word out amongst friends and families so they know what you&#39;re looking for.</li>
</ol>
<p>Worst case scenario, you get to learn whether or not self-employment is really something for you, and hopefully get to earn some extra money on the side.
Best case scenario, you&#39;ll be laying down the foundation of your new business and a path to freedom!
I&#39;m very excited for you and wish you all the best of luck!
If you choose to go on this journey, please <a href="https://www.codingwithjesse.com/contact/me/">let me know and keep me updated</a>!</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/2025-01-14-full-time-to-freelance.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Dear Recruiter: I am not interested in a full-time job]]></title>
            <link>https://www.codingwithjesse.com/blog/dear-recruiter/</link>
            <guid>https://www.codingwithjesse.com/blog/dear-recruiter/</guid>
            <pubDate>Tue, 07 Jan 2025 19:06:59 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2025-01-07-dear-recruiter.jpg" width="800" height="400" alt="Dozens of cubicles in a depressing office. Photo by kate.sade on Unsplash."/>

<p>Dear Recruiter,</p>
<p>Thank you for contacting me, but unfortunately I am not interested in a full-time job.
There is no way in hell you&#39;ll find me in an office nine-to-five Monday-to-Friday.
I&#39;ve been there, done that, and it was a living nightmare.</p>
<p>No, you don&#39;t need to circle back in three months to see if my situation has changed.
I&#39;m not available for employment, and I never will be.</p>
<p>Yes, I understand that the position offers benefits.
My position offers many more benefits.
I can work whenever I want, wherever I want, and for whomever I want.
I can have a two-hour nap in the middle of the afternoon if I want to.
I can go out with my wife for three hours on a random morning.
I can pick up my children from school and go to a playground.</p>
<p>You think it&#39;s cool that you have a daily standup meeting at 10am?
I haven&#39;t had a meeting in months.
I have nothing work-related in my calendar at all.
I talk to my clients over email.
Sometimes I ask them for a phone call, and I&#39;m always happy to hear their voices.
They leave me alone to do my best work and they never give me deadlines.
They have no expectations for me on any given day.
They trust that I&#39;ll make good, steady progress every week.
They know that in an emergency, I&#39;ll drop everything to help them.</p>
<p>Your company is offering a competitive salary?
I make more than that working part-time.
I raise my rates every few years and simply let my clients know.
Sometimes they choose to stop working with me, which is sad.
But it&#39;s okay because I have other clients who happily pay more.
It&#39;s not as tragic as your upcoming layoffs would have been.</p>
<p>Your company has management opportunities?
Sounds terrible.
I work one-on-one with business owners who I&#39;ve worked with for decades.
We trust each other and have become good friends.
I understand what they want and need, and the trade-offs they&#39;re willing to make.
There&#39;s no game of telephone trying to guess what upper management wants.
No waiting for approval from board meetings.
No politics at all.</p>
<p>Your company will pay for me to move to another city?
You must be joking.
I work from my home, and I always will.
Well, sometimes I choose to work on an airplane, or in a waiting room, or in a coffee shop.
One place you&#39;ll never find me is at a desk in an office building.</p>
<p>So no, I&#39;ll never be interested in joining your growing team.
I&#39;m in a very happy long-term relationship with my clients.
There&#39;s no way I&#39;m going to leave them high-and-dry to become a cog in your machine.</p>
<p>I wish you good luck on your search.
And I wish all the best to whomever you manage to enslave.</p>
<p>Sincerely,<br>
Jesse Skinner<br>
Freelance Web Developer</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/2025-01-07-dear-recruiter.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How I empower my clients to make changes]]></title>
            <link>https://www.codingwithjesse.com/blog/empower-your-clients-to-make-changes/</link>
            <guid>https://www.codingwithjesse.com/blog/empower-your-clients-to-make-changes/</guid>
            <pubDate>Tue, 10 Dec 2024 15:51:34 GMT</pubDate>
            <content:encoded><![CDATA[<img src="https://www.codingwithjesse.com/images/2024-12-10-empower-your-clients.jpg" width="800" height="451" alt="A child fishing"/>
<cite>Photo by <a href="https://unsplash.com/@btwilkins?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Ben Wilkins</a> on <a href="https://unsplash.com/photos/toddler-wearing-gray-hoodie-holding-fishing-rod-WH2UgbxLBs4?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></cite>

<p>When a client asks me to make a change to a website, I have two choices.
I can go and make the change.
Or, I can set things up so that the client can make the change themselves.</p>
<p>It&#39;s not always obvious which of these two will be a better use of my time.
If it&#39;s a one-time change that will never need to happen again, of course it&#39;s better if I go make the change.
But sometimes it&#39;s obviously something that will need to be changed again and again.</p>
<p>Sometimes the change is so small that it&#39;s a quick copy &amp; paste, so there&#39;s almost no benefit to adding a content management system.
Often, the changes can be more complex, and require a lot of back-and-forth communication, asking for clarification, testing, reviewing, etc.
I have a pretty low tolerance for this before I insist on building a tool for my client instead.</p>
<p>Pretty much every system should have some kind of administrative login area, where the client can go in and access these admin-only tools.
It might be as complex as a content management system, where they can create whole new pages, and change all the text in the system.
It might be where they manage other users, or get the answers to questions they might have otherwise asked me for.</p>
<p>In one case, I had built a system that had a complex set of rules, basically if/then statements, and it was evolving rapidly.
Every week I was being asked to make changes to the logic, and also asked to remind the client about what it was already doing.
So, in a few days, I designed and developed a Domain-Specific Language (DSL) that allowed all of the logic to be captured in a single text file.
It wasn&#39;t a programming language, because my client isn&#39;t a programmer.
Rather, I designed it to be intuitive for him, so it would be easy for him to read, edit and publish changes to the logic in the system.
Many years later, he is still actively working with this same DSL.</p>
<p>I have another client who is more technical, and has some experience with basic HTML and CSS.
They wanted to make broad changes to the design of the site, and to the text that was hardcoded throughout.
I gave them two options: either they could provide me with a long list of these changes, or I could bring them in to collaborate on the code.
They had no experience with git, GitHub, Svelte, or even with using an IDE.
I thought it was worth a try, so I sent them a long list of instructions for installing all the software they&#39;d need.
I sent them a few YouTube videos about using VS Code with Git, and had them create a GitHub account.
I explained how the site was architected, and the basics of Svelte components.</p>
<p>Within a week, they were making changes to CSS and text and committing and pushing changes to GitHub!
It was so exciting!
Just looking at the long git diff, there were literally hundreds of changes.
If they had tried to write out those changes in a Google doc, it would have required dozens of screenshots.
It honestly would have been painful for me to carefully go through and implement all of the tiny changes.
And chances are, I would have screwed up more than once, and they would have had to make new screenshots.
They would&#39;ve felt like a bother to ask me to increase the font size by 5%, or change a border to a different shade of grey.
More than likely, they would have just accepted my poor attempt as &quot;good enough&quot; and said thanks.</p>
<p>Instead, they are now empowered to go in and make changes themselves whenever they want.
As the site evolves, over the coming decade, they&#39;ll have the confidence to go in and make all the updates they need.
They were even able to use AI to generate a snippet of Svelte code in order to add a second button to the page!</p>
<p>No, I&#39;m not worried about my job going away.
I&#39;m happy to be focusing on the hard things, and empower my clients to do as much as they can on their own.
They are delighted to not have to &quot;bother me&quot; for small changes when I&#39;m working on bigger features for them.
They&#39;re able to save money and time.
It&#39;s truly a win-win.</p>
<p>Another client had a very complex system that was built by a third-party vendor and written in Python.
They brought me in to help simplify the system.
In this case, they actually did have developers on the team, but the developers didn&#39;t have much Python experience.
I actually rewrote the entire system in TypeScript.
It was a fantastic opportunity to clean things up and simplify.
I wrote it in a way that it would be intuitive to them, so the training only took an hour or two.
I was delighted to see the developers confidently making changes to the system themselves for the first time ever, and with ease.</p>
<p>A Chinese proverb says, &quot;Give a man a fish, and you feed him for a day. Teach a man to fish, and you feed him for a lifetime.&quot;
I say, &quot;Make a change for a client, they&#39;ll be happy for a day. Empower your clients to make changes themselves, they&#39;ll be happy for a lifetime.&quot;</p>
]]></content:encoded>
            <enclosure url="https://www.codingwithjesse.com/images/2024-12-10-empower-your-clients.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>