<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Dean Hume's Blog]]></title><description><![CDATA[I'm a Technical Program Manager at Xbox Game Studios. I write about technical leadership, developer tools, and making great games.]]></description><link>https://deanhume.com/</link><image><url>https://deanhume.com/dean-hume-headshot.jpg</url><title>Dean Hume&apos;s Blog</title><link>https://deanhume.com/</link></image><generator>dlog</generator><lastBuildDate>Thu, 08 Oct 2026 00:00:00 GMT</lastBuildDate><atom:link href="https://deanhume.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[A Modern Guide to Using OAuth 2.0 with Node.js]]></title><description><![CDATA[Get an OAuth 2.0 access token in Node.js with MSAL, then call Microsoft Graph using built-in fetch and credentials loaded from an environment file.]]></description><link>https://deanhume.com/a-modern-guide-to-using-oauth-2-0-with-node-js/</link><guid isPermaLink="false">https://deanhume.com/a-modern-guide-to-using-oauth-2-0-with-node-js/</guid><category><![CDATA[OAuth]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate><media:content url="https://deanhume.com/content/posts/a-modern-guide-to-using-oauth-2-0-with-node-js/nodejs-oauth-1160.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/a-modern-guide-to-using-oauth-2-0-with-node-js/nodejs-oauth-1160.webp" alt="A Modern Guide to Using OAuth 2.0 with Node.js"><p>Ever followed an OAuth tutorial, copied the code, and then found out the library it uses was deprecated years ago? Yeah, me too. It&#39;s frustrating - especially when all you wanted was an access token.</p>
<p>A little while ago I wrote <a href="https://deanhume.com/a-modern-guide-to-using-oauth-2-0-with-c/">A Modern Guide to Using OAuth 2.0 with C# and Visual Studio Code</a>, and it got a lot more attention than I expected. A few people asked if I could do the same thing for Node.js, so here it is.</p>
<p>This guide walks you through implementing OAuth 2.0 in Node.js using modern libraries and built-in features. No extra HTTP client, no config library - just a handful of lines of code and a working token at the end of it.</p>
<p>Whether you&#39;re building a script, a back-end service, or an MCP server that needs to talk to a protected API (more on that in a future post), this is a clean place to start.</p>
<h2 id="on-this-page">On this page</h2>
<ol>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#step-1-create-a-new-nodejs-project">Step 1: Create a New Node.js Project</a></li>
<li><a href="#step-2-install-required-packages">Step 2: Install Required Packages</a></li>
<li><a href="#step-3-configure-your-oauth-settings">Step 3: Configure Your OAuth Settings</a></li>
<li><a href="#step-4-authenticate-and-acquire-a-token">Step 4: Authenticate and Acquire a Token</a></li>
<li><a href="#step-5-make-an-authenticated-api-call">Step 5: Make an Authenticated API Call</a></li>
<li><a href="#optional-set-up-an-entra-id-test-oauth-app">Optional: Set up an Entra ID test OAuth App</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ol>
<hr>
<h3 id="prerequisites">Prerequisites</h3>
<p>Before we dive in, make sure you have the following:</p>
<ul>
<li><a href="https://nodejs.org/">Node.js</a> 22 or later (anything from 20.6 will work for this guide, but I&#39;d go with the current LTS)</li>
<li><a href="https://code.visualstudio.com/">Visual Studio Code</a></li>
<li>A basic understanding of HTTP and REST APIs</li>
<li>A registered OAuth 2.0 application (e.g. via Google, Microsoft, or a custom provider)</li>
</ul>
<p>Let&#39;s get started!</p>
<hr>
<h3 id="step-1-create-a-new-node-js-project">Step 1: Create a New Node.js Project</h3>
<p>Open your terminal and run:</p>
<pre><code class="language-shell">mkdir oauth-node-demo
cd oauth-node-demo
npm init -y
npm pkg set type=module</code></pre>
<p>That last line switches the project to ES modules, which means we can use <code>import</code> and top-level <code>await</code> without any extra setup.</p>
<hr>
<h3 id="step-2-install-required-packages">Step 2: Install Required Packages</h3>
<p>Since I&#39;ve already got an Entra app setup, we&#39;ll use <code>@azure/msal-node</code> to handle the OAuth flow for this example:</p>
<pre><code class="language-shell">npm install @azure/msal-node</code></pre>
<p>That&#39;s the only dependency. Node.js 18 and above ships with <code>fetch</code> built in, so we don&#39;t need <code>node-fetch</code> or anything else to make API calls. Since Node 20.6 can load <code>.env</code> files natively, we don&#39;t need <code>dotenv</code> either.</p>
<hr>
<h3 id="step-3-configure-your-oauth-settings">Step 3: Configure Your OAuth Settings</h3>
<p>Create a <code>.env</code> file in the root of your project to store your credentials:</p>
<pre><code class="language-shell">CLIENT_ID=your-client-id
TENANT_ID=your-tenant-id
CLIENT_SECRET=your-client-secret
SCOPES=https://graph.microsoft.com/.default</code></pre>
<blockquote>🔍 <strong>What is <code>tenant-id</code>?</strong></blockquote>
<blockquote>This is the unique identifier (GUID) for your Microsoft Entra ID tenant. You can find it in the Azure portal under Microsoft Entra ID &gt; Overview. Alternatively, you can use your domain name (e.g. <code>contoso.onmicrosoft.com</code>) in place of the GUID. This might be different depending on your OAuth provider.</blockquote>
<blockquote>⚠️ <strong>Keep your secrets out of source control.</strong></blockquote>
<blockquote>Add <code>.env</code> to your <code>.gitignore</code> before you do anything else. I&#39;ve seen more than one client secret accidentally pushed to a public repo, and it&#39;s not a fun clean-up job.</blockquote>
<pre><code class="language-shell">echo &quot;.env&quot; &gt;&gt; .gitignore</code></pre>
<hr>
<h3 id="step-4-authenticate-and-acquire-a-token">Step 4: Authenticate and Acquire a Token</h3>
<p>Create a file called <code>index.js</code> and add the following. It uses the client credentials flow, which is the right choice when your app is calling an API as itself rather than on behalf of a signed-in user:</p>
<pre><code class="language-javascript">import { ConfidentialClientApplication } from &quot;@azure/msal-node&quot;;

const { CLIENT_ID, TENANT_ID, CLIENT_SECRET, SCOPES } = process.env;

const msalClient = new ConfidentialClientApplication({
  auth: {
    clientId: CLIENT_ID,
    authority: `https://login.microsoftonline.com/${TENANT_ID}`,
    clientSecret: CLIENT_SECRET,
  },
});

const result = await msalClient.acquireTokenByClientCredential({
  scopes: SCOPES.split(&quot;,&quot;),
});

console.log(`Access Token: ${result.accessToken}`);</code></pre>
<p>Run it with the <code>--env-file</code> flag so Node loads your <code>.env</code> file:</p>
<pre><code class="language-shell">node --env-file=.env index.js</code></pre>
<p>If everything is set up correctly, you&#39;ll see a long access token printed to the console. Congratulations - you just authenticated with OAuth 2.0!</p>
<p>A quick note on tokens: MSAL caches them in memory for you, so calling <code>acquireTokenByClientCredential</code> again will return the cached token until it&#39;s close to expiring. You don&#39;t need to write your own refresh logic for this flow.</p>
<hr>
<h3 id="step-5-make-an-authenticated-api-call">Step 5: Make an Authenticated API Call</h3>
<p>Now let&#39;s use that token to call a protected resource. Add this to the bottom of <code>index.js</code>:</p>
<pre><code class="language-javascript">const response = await fetch(&quot;https://graph.microsoft.com/v1.0/users&quot;, {
  headers: {
    Authorization: `Bearer ${result.accessToken}`,
  },
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}

const data = await response.json();
console.log(JSON.stringify(data, null, 2));</code></pre>
<blockquote>✅ <strong>Testing endpoint:</strong> <code>https://graph.microsoft.com/v1.0/users</code></blockquote>
<blockquote>This endpoint returns a list of users in your Entra ID tenant. To use it, make sure your app registration has the <code>User.Read.All</code> application permission and that you&#39;ve granted admin consent.</blockquote>
<p>Run it again:</p>
<pre><code class="language-shell">node --env-file=.env index.js</code></pre>
<p>If all works as expected, you should see a response similar to this:</p>
<pre><code class="language-json">{
  &quot;@odata.context&quot;: &quot;https://graph.microsoft.com/v1.0/$metadata#users&quot;,
  &quot;value&quot;: [
    {
      &quot;businessPhones&quot;: [],
      &quot;displayName&quot;: &quot;Dean Hume&quot;,
      &quot;givenName&quot;: &quot;Dean&quot;,
      &quot;jobTitle&quot;: null,
      &quot;mail&quot;: null,
      &quot;mobilePhone&quot;: null,
      &quot;officeLocation&quot;: null,
      &quot;preferredLanguage&quot;: &quot;en&quot;,
      &quot;surname&quot;: &quot;Hume&quot;,
      &quot;userPrincipalName&quot;: &quot;email.com#EXT#@email.onmicrosoft.com&quot;,
      &quot;id&quot;: &quot;id-response-goes-here&quot;
    }
  ]
}</code></pre>
<p>If you get a <code>403</code> instead, it&#39;s almost always one of two things - the permission hasn&#39;t been added to your app registration, or admin consent hasn&#39;t been granted. I&#39;ve lost more time to that second one than I&#39;d like to admit.</p>
<hr>
<h3 id="optional-set-up-an-entra-id-test-oauth-app">Optional: Set up an Entra ID test OAuth App</h3>
<p>This step is optional, as you might want to use your own OAuth provider. However, I wanted to be sure that my code actually worked, so I set up an app in <a href="https://www.microsoft.com/en-gb/security/business/identity-access/microsoft-entra-id">Microsoft Entra ID</a> to test with.</p>
<p>If you&#39;d like to do the same, these resources should help:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/entra/fundamentals/create-new-tenant">Quickstart: Create a new tenant in Microsoft Entra ID</a></li>
<li><a href="https://www.spletzer.com/2025/01/how-to-get-your-client-id-and-client-secret-from-entra-id/">Client Id &amp; Secret from Entra ID</a></li>
</ul>
<hr>
<h3 id="conclusion">Conclusion</h3>
<p>OAuth 2.0 doesn&#39;t have to be intimidating. With a modern library like MSAL and the features that now ship with Node.js, you can securely authenticate and call a protected API in well under 30 lines of code - and with only one dependency.</p>
<p>Have you run into any OAuth headaches when working with Node.js? I&#39;d love to hear about them in the comments or over on GitHub - and if there&#39;s a flow you&#39;d like me to cover next, let me know.</p>]]></content:encoded></item><item><title><![CDATA[How AI helped me get my website to a perfect Lighthouse score]]></title><description><![CDATA[How I used GitHub Copilot and Lighthouse reports to improve my blog's performance, taking some of its worst pages from a score of 73 to 100.]]></description><link>https://deanhume.com/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score/</link><guid isPermaLink="false">https://deanhume.com/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score/</guid><category><![CDATA[AI]]></category><category><![CDATA[Web Performance]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><media:content url="https://deanhume.com/content/posts/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score-1280.webp" alt="How AI helped me get a perfect Lighthouse score"><p>I&#39;ve been into web performance for as long as I can remember. Back in the day, I cared about it enough to write a book, <a href="https://www.manning.com/books/fast-asp-dot-net-websites">Fast ASP.NET Websites</a>. My career has since moved into gaming, and while I&#39;ve kept an eye on web performance, I haven&#39;t stayed as up to date as I&#39;d like.</p>
<p>A few of the worst-performing pages on this blog were sitting at a Lighthouse score of 73. I&#39;d been putting off tackling them, but after making some changes to the blog recently, I tried something different: I handed the performance problem to GitHub Copilot, using a mix of Opus and GPT Astra.</p>
<p>I gave AI the Lighthouse output, pointed it at my theme, and asked it to work through the biggest problems first.</p>
<p>I now have a Lighthouse score of 100 on <em>almost</em> all pages including mobile.</p>
<figure><img src="https://deanhume.com/content/posts/how-ai-helped-me-get-my-website-to-a-perfect-lighthouse-score/perfect-lighthouse-score.png" alt="Perfect Lighthouse Score" loading="lazy"></figure>
<p>It still needs testing on real-world devices, but I&#39;ve never quite been able to achieve that in the past without a lot of pain.</p>
<h2 id="where-it-got-it-wrong">Where it got it wrong</h2>
<p>It didn&#39;t get it perfect the first time round, but it did a pretty amazing job. The AI assumed SVG would be the smallest and best image format for many of the pages. It&#39;s a reasonable guess - SVGs are brilliant for the right job - but when I actually tested it, AVIF and WebP came out smaller for my images.</p>
<p>AVIF are a new format for me personally, although they have been around for a while and are well supported.</p>
<p>If I hadn&#39;t checked, I&#39;d have shipped a slower site and felt very pleased with myself 😂</p>
<h2 id="smaller-images-measured-rather-than-assumed">Smaller images, measured rather than assumed</h2>
<p>On other pages, AI suggested that the feature image changed from SVG to PNG so it could go through the image build, which already generated responsive WebP versions.</p>
<p>Rather than assume AVIF would win for every image, the build now compares the files. Every generated AVIF width has to be at least 10% smaller than its WebP equivalent. If any width fails that comparison, the site sticks with WebP for that image.</p>
<p>Where AVIF meets the threshold, a <code>&lt;picture&gt;</code> element offers it to the browser with a WebP fallback. The size comparison happens during the build, so the browser doesn&#39;t need to download both formats to decide which to use.</p>
<h2 id="load-the-important-image-first">Load the important image first</h2>
<p>Smaller files were only part of the work. I also had to think about the order images load in.</p>
<p>On some pages (for example <a href="https://deanhume.com/tag/game-development/">this one</a>), the first image a visitor sees is in the top article card. But it was set to load late, the same as every other image on the page. That meant the most noticeable thing on screen was one of the last things to show up.</p>
<p>The fix was to load that first image straight away and let the rest load as the visitor scrolls. Pages that already have a header image at the top work as before. That first image is a likely candidate for Largest Contentful Paint, or LCP: the metric that tracks when the largest visible image or block of text appears. It&#39;s not a good candidate for delaying until later.</p>
<p>The template now gives that image <code>loading=&quot;eager&quot;</code> and <code>fetchpriority=&quot;high&quot;</code>. The remaining cards stay lazy-loaded. If the tag already has a header image, the first card doesn&#39;t get that extra priority.</p>
<p>You don&#39;t need every image to load fast. You need the one people see first to load fast.</p>
<h2 id="shorten-the-font-request-chain">Shorten the font request chain</h2>
<p>Lighthouse also flagged a chain of critical requests. For the main font, the browser had to discover the stylesheet, then discover the font through that CSS.</p>
<p>Adding a preload before the stylesheet makes the font discoverable directly from the HTML:</p>
<pre><code class="language-html">&lt;link rel=&quot;preload&quot; href=&quot;/inter-latin.woff2&quot;
      as=&quot;font&quot; type=&quot;font/woff2&quot; crossorigin&gt;</code></pre>
<p>Only the main Inter Latin font gets that preload. The other fonts still load when needed, and <code>font-display: swap</code> stays in place so text can appear in a fallback font while the download completes.</p>
<h2 id="what-to-take-from-this">What to take from this</h2>
<p>If you&#39;ve been putting off tricky web performance work on your site, give AI the actual lighthouse report or performance metrics rather than just asking it to &quot;make the site faster&quot;. That gives it somewhere useful to start, and gives you specific changes to check.</p>
<p>AI helped me act on performance problems I&#39;d been putting off. The final result still needed a little testing, but I&#39;m really glad with how things turned out!</p>]]></content:encoded></item><item><title><![CDATA[Prompt-Driven Development: Building with GitHub Copilot Coding Agents]]></title><description><![CDATA[We used GitHub Copilot coding agents to build an app across different time zones. The AI wrote the code, but prompts, tests, guardrails, and pull requests made it software development rather than vibe coding.]]></description><link>https://deanhume.com/prompt-driven-development-building-with-github-copilot-coding-agents/</link><guid isPermaLink="false">https://deanhume.com/prompt-driven-development-building-with-github-copilot-coding-agents/</guid><category><![CDATA[AI]]></category><category><![CDATA[Programming]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Wed, 23 Sep 2026 17:52:00 GMT</pubDate><media:content url="https://deanhume.com/content/posts/prompt-driven-development-building-with-github-copilot-coding-agents/prompt-driven-development-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/prompt-driven-development-building-with-github-copilot-coding-agents/prompt-driven-development-1280.webp" alt="Prompt-driven development"><p>At Microsoft, we host a global hackathon once a year. It&#39;s a great opportunity for people to come together, experiment with ideas, and build things that might make life a little easier.</p>
<p>Hackathons have traditionally attracted people who are comfortable writing code. You might have a designer or subject matter expert on the team, but sooner or later an idea would need to pass through a developer before it became a working feature.</p>
<p>AI is beginning to change that.</p>
<p>For our most recent hackathon, I worked with colleagues in different time zones to build an application using GitHub Copilot coding agents. Some of the people contributing weren&#39;t developers, yet they were still able to take an idea, describe the behaviour they wanted, and turn it into working software.</p>
<p>The AI generated the code, but I wouldn&#39;t describe what we did as vibe coding.</p>
<p>We had unit tests. We had repository instructions and skills. We worked through pull requests, and we used GitHub Copilot to review the changes before they were merged.</p>
<p>The prompts might have driven the development, but engineering practices kept it on the road. At the end of the Hackathon, I was proud to say that we had a working product that wasn&#39;t that far off from launch.</p>
<h2 id="what-is-prompt-driven-development">What is prompt-driven development?</h2>
<p>This got me thinking about a post that I read lately from <a href="https://seldo.com/posts/we-are-all-product-engineers-now/">Laurie Voss</a> where he suggests how those of us who use AI to build are all product engineers now, whether we like it or not. The cost of writing code has collapsed, and the way we used to produce it no longer exists in the same way it did before AI.</p>
<p>I&#39;ve been using this term &quot;prompt driven development&quot; for a while now and I feel like it perfectly describes how a lot of modern AI first development takes place.</p>
<p>Prompt-driven development isn&#39;t a universally agreed methodology. It&#39;s the phrase I&#39;ve started using to describe the way we worked:</p>
<blockquote>Prompt-driven development uses structured natural-language instructions to direct coding agents, while tests, guardrails, and human review continuously verify the result.</blockquote>
<p>The important part of that definition isn&#39;t the prompt. It&#39;s everything surrounding it.</p>
<p>If I ask an agent to build a feature and merge whatever it produces without understanding or verifying it, that&#39;s much closer to vibe coding. I am trusting the output because it looks plausible and the application appears to work.</p>
<p>In prompt-driven development, the prompt starts the work. It doesn&#39;t decide whether the work is finished.</p>
<p>A useful prompt still needs a clear outcome. The repository supplies the coding standards and constraints. Automated tests check the behaviour. A pull request makes the change visible, and a review gives the team another opportunity to find problems.</p>
<p>The AI writes the code inside a system that the team has deliberately designed.</p>
<h2 id="a-prompt-could-be-surprisingly-simple">A prompt could be surprisingly simple</h2>
<p>During the Hackathon, one of the features we needed was the ability to filter a collection of data. The request was roughly:</p>
<blockquote>Add dropdown filters for location and project.</blockquote>
<p>On its own, that isn&#39;t a detailed software specification. It doesn&#39;t describe the framework, file structure, naming conventions, test runner, or how the filters should interact.</p>
<p>However, the agent wasn&#39;t starting with an empty chat window.</p>
<p>We had already added <a href="https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions">repository custom instructions</a> that explained how the project should be developed. We also had <a href="https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills">agent skills</a> that gave Copilot a repeatable workflow for particular tasks.</p>
<p>Most importantly, the project followed test-driven development:</p>
<figure><img src="https://deanhume.com/content/posts/prompt-driven-development-building-with-github-copilot-coding-agents/prompt-driven-development-test-cycle.svg" alt="Test-driven development cycle: write a failing unit test, confirm that it fails for the expected reason, implement the smallest change that makes it pass, then run the full test suite" loading="lazy"></figure>
<p>That context turned a short prompt into a constrained task. The agent knew that it couldn&#39;t simply add a couple of dropdowns and declare victory. It first needed to express the behaviour in a test, then produce an implementation that satisfied it.</p>
<p>I&#39;ve previously written about why <a href="https://deanhume.com/why-short-ai-coding-prompts-can-cost-you-more-time/">short AI coding prompts can cost you more time</a>. This experience didn&#39;t change my mind about that. A short prompt only worked here because the missing context already existed in the repository.</p>
<p>The real instruction wasn&#39;t one sentence. It was the prompt plus the codebase, tests, instructions, and skills.</p>
<h2 id="building-as-a-distributed-team">Building as a distributed team</h2>
<p>Our team was spread across different time zones, so we couldn&#39;t rely on everyone being online together. The project needed to make sense to somebody arriving several hours after a decision had been made.</p>
<p>Coding agents helped reduce that dependency on real-time collaboration. A team member could describe a feature, ask the agent to implement it, and leave the resulting tests and pull request for the next person to review. The next colleague didn&#39;t need a meeting before they could understand what had changed or continue the work.</p>
<p>This didn&#39;t remove the need to communicate. It changed where that communication happened.</p>
<p>Instead of knowledge living only in a call or chat message, more of it became part of the repository:</p>
<ul>
<li>The prompt recorded the intended outcome.</li>
<li>The instructions and skills recorded how the agent should work.</li>
<li>The tests recorded the expected behaviour.</li>
<li>The pull request recorded what changed and why.</li>
<li>The review recorded the questions and issues that still needed attention.</li>
</ul>
<p>That made the repository a shared workspace for both people and agents. It gave us a common source of context when our working hours only partially overlapped.</p>
<h2 id="the-tests-became-a-shared-safety-net">The tests became a shared safety net</h2>
<p>Our tests gave us a shared definition of what the application was supposed to do. When an agent added a feature, it had to preserve the behaviour that was already covered.</p>
<p>This was especially important because not everyone on the team was technical. A colleague didn&#39;t need to inspect every implementation detail to see whether a change had broken an existing feature. The test suite gave them an immediate signal.</p>
<p>Tests didn&#39;t prove that every change was perfect. They did give both the people and the agents a safer place to work.</p>
<h2 id="pull-requests-became-our-handoff">Pull requests became our handoff</h2>
<p>Instead of allowing agents to change the main branch directly, each piece of work arrived as a pull request. It captured the code, the tests, and the reason for the change in one visible proposal.</p>
<p>We also requested <a href="https://docs.github.com/en/copilot/concepts/agents/code-review">GitHub Copilot code reviews</a> on the pull requests. The reviews picked up real issues and helped us correct them before merging.</p>
<p>For non-technical members of the team, this added another useful layer. They could ask the agent to implement a feature, then use Copilot code review to examine the result. They didn&#39;t need to pretend that they understood every line of code, and they weren&#39;t limited to accepting the first output the agent produced.</p>
<p>That doesn&#39;t mean an AI review makes human review unnecessary. AI-generated code deserves the same scrutiny as any other contribution. However, during a time-limited hackathon, Copilot gave the whole team access to feedback that might otherwise have depended on one technical person being online.</p>
<p>The workflow looked something like this:</p>
<ol>
<li>A team member described the behaviour they wanted.</li>
<li>The coding agent created a branch and implemented the change.</li>
<li>The repository instructions and skills guided how it worked.</li>
<li>Unit tests checked the new and existing behaviour.</li>
<li>The agent opened a pull request.</li>
<li>Copilot reviewed the change and identified potential issues.</li>
<li>We addressed the feedback before merging.</li>
</ol>
<h2 id="non-technical-didnt-mean-non-contributing">Non-technical didn&#39;t mean non-contributing</h2>
<p>One of the most interesting parts of the hackathon was seeing how much a non-technical colleague could contribute.</p>
<p>They understood the problem we were trying to solve. They knew what information people needed, which workflows felt awkward, and what useful behaviour should look like. Previously, that knowledge might have been written into a requirement and passed to a developer.</p>
<p>With coding agents, they could express that intent directly.</p>
<figure><img src="https://deanhume.com/content/posts/prompt-driven-development-building-with-github-copilot-coding-agents/prompt-driven-development-non-technical.png" alt="Prompt Driven Development for Non Technical folks" loading="lazy"></figure>
<p>This didn&#39;t suddenly turn every team member into a software engineer, and it didn&#39;t remove the value of technical experience. Somebody still needed to create the initial structure, choose the testing approach, and put the guardrails in place.</p>
<p>But once that paved path existed, more people could safely move along it.</p>
<p>I think this is one of the most useful possibilities opened up by coding agents. They don&#39;t just help experienced developers write code faster. They can shorten the distance between somebody who understands a problem and a working version of their idea.</p>
<h2 id="why-i-dont-call-this-vibe-coding">Why I don&#39;t call this vibe coding</h2>
<p>The distinction isn&#39;t whether AI wrote some of the code or all of it. In our case, AI generated the code, but that tells you very little about the quality of the development process.</p>
<p>The difference was how we decided to trust a change.</p>
<p>We didn&#39;t trust it because the agent sounded confident. We didn&#39;t trust it because the interface looked right in a quick demo. We trusted it enough to continue because the expected behaviour had been written down, the tests passed, the existing tests still passed, and the change had gone through a pull request and review.</p>
<p>For me, prompt-driven development has four parts:</p>
<figure><img src="https://deanhume.com/content/posts/prompt-driven-development-building-with-github-copilot-coding-agents/four-parts-of-prompt-driven-development.svg" alt="The four connected parts of prompt-driven development: intent defines the desired outcome, context teaches the agent how the repository works, verification tests the behaviour, and review challenges the result" loading="lazy"></figure>
<p>Remove the verification and review, and prompt-driven development can quickly collapse into vibe coding with a more professional name.</p>
<h2 id="what-id-take-into-the-next-hackathon">What I&#39;d take into the next hackathon</h2>
<p>The biggest lesson wasn&#39;t that AI could generate an entire application. I expected the agents to write code.</p>
<p>What surprised me was how effectively people with different levels of technical experience could work together when the repository contained the right boundaries. The instructions, skills, tests, and pull-request workflow gave us a common way of working, even when we weren&#39;t online at the same time.</p>
<p>If I were starting another prompt-driven project, I would establish those foundations before asking an agent to build features:</p>
<ol>
<li>Write down the project&#39;s coding and architectural rules.</li>
<li>Give agents a repeatable test-driven workflow.</li>
<li>Protect the main branch and make changes through pull requests.</li>
<li>Run the complete test suite for every change.</li>
<li>Use code review, but don&#39;t treat an AI review as unquestionable.</li>
<li>Make the expected behaviour understandable to technical and non-technical contributors.</li>
</ol>
<p>AI made our hackathon more accessible, but the guardrails made that access useful. At the end of the hackathon, we had a project that was solid and could actually be used going forward instead of a sloppy vibe coded project.</p>
<p>The AI wrote the code, but the best part was that the team still engineered the application.</p>]]></content:encoded></item><item><title><![CDATA[What a Technical Program Manager actually does]]></title><description><![CDATA[What does a Technical Program Manager actually do? A look at the gaps they fill, the decisions they chase, and the reactive vs proactive split.]]></description><link>https://deanhume.com/what-a-technical-program-manager-actually-does/</link><guid isPermaLink="false">https://deanhume.com/what-a-technical-program-manager-actually-does/</guid><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Mon, 14 Sep 2026 15:54:31 GMT</pubDate><media:content url="https://deanhume.com/content/posts/what-a-technical-program-manager-actually-does/technical-program-manager-role-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/what-a-technical-program-manager-actually-does/technical-program-manager-role-1280.webp" alt="What a Technical Program Manager actually does"><p>There is a particular moment when I meet someone new and they ask what I do for a living.</p>
<p>“<em>I’m a Technical Program Manager.</em>”</p>
<p>You can usually see the follow-up question forming before I’ve finished the sentence.</p>
<p>“<em>Cool. What is a Technical Program Manager?</em>”</p>
<p>If I explain it using words like alignment, execution, stakeholders and cross-functional delivery, their eyes just glaze over. If I say I work with engineers but I’m not an engineer - and that I work on delivery but I’m not quite a producer or project manager - I haven’t exactly cleared things up. Sometimes, I might just say &quot;<em>it&#39;s like a technical project manager</em>&quot; and leave it there - but that doesn&#39;t feel quite right either.</p>
<p>The difficulty is that Technical Program Manager (TPM) work often sits in the gaps between well-understood jobs. Those gaps are also where some of the most expensive problems begin.</p>
<h2 id="first-what-a-tpm-is-not">First, what a TPM is not</h2>
<p>A TPM is not simply a scrum master, a project tracker with a fancier title, or the person responsible for making sure meetings happen.</p>
<p>Those activities might appear in the job. There may be plans to maintain, meetings to run and actions to chase. However, treating those things as the role is a bit like describing software engineering as typing.</p>
<p>The useful part of the job is not recording that a problem exists. It is understanding the technical and organisational system well enough to spot why the problem exists, who can change it and what needs to happen next.</p>
<h2 id="a-tpm-finds-the-gaps-between-teams">A TPM finds the gaps between teams</h2>
<p>Imagine two engineering teams working toward the same release.</p>
<p>One team owns the build. The other owns the platform integration. Both teams might believe the other is responsible for a certification step. However, when you look at it on separate project plans, everything might look green and on track because neither plan contains a task that appears late.</p>
<p>That&#39;s until the project reaches a point where it stops due to no one owning the certification step. The problem isn&#39;t that somebody forgot to update a project plan. It&#39;s that ownership was never made explicit across the boundary between the teams.</p>
<p>This is where a TPM comes in. They should find that gap before it becomes a <a href="https://deanhume.com/why-readiness-should-be-a-habit-not-a-final-gate">release-blocking surprise</a>. That means asking questions that can feel almost annoyingly specific:</p>
<ul>
<li>Who owns this step?</li>
<li>What input do they need?</li>
<li>When will they receive it?</li>
<li>What happens if it is late?</li>
<li>Has the receiving team agreed to the date?</li>
<li>Is this dependency visible in both teams’ plans, or only assumed in conversation?</li>
</ul>
<p>None of those questions is technically sophisticated on its own. The skill is knowing where to ask them, understanding the answers and noticing when two reasonable answers do not fit together.</p>
<h2 id="a-tpm-translates-risk-into-a-decision">A TPM translates risk into a decision</h2>
<p>Technical teams are great at describing technical problems. They might explain that code isn&#39;t performant, or a build pipeline has become unreliable, or even that an integration that depends on an API that is still changing.</p>
<p>That does not automatically produce action.</p>
<p>“This code isn&#39;t hitting performance targets” may be accurate, but it leaves a senior leader with several unanswered questions. What could it delay? When do we need to decide? What are the options? What does each option cost? Is this something the team can absorb, or does it threaten a committed milestone?</p>
<figure><img src="https://deanhume.com/content/posts/what-a-technical-program-manager-actually-does/technical-program-manager-risk.jpg" alt="A TPM turns the technical risk into a decision that someone can make" loading="lazy"></figure>
<p>A TPM turns the technical risk into a decision that someone can make:</p>
<blockquote>If the code isn&#39;t hitting performance targets by Friday, the integration team will lose its final test window. We can reduce the launch scope now, add engineering support to the team, or accept a likely delay. We need a decision by Wednesday because each option changes next week’s plan.</blockquote>
<p>The TPM has not replaced the engineer’s judgment. They have preserved it, added the missing programme context and made the consequence difficult to ignore.</p>
<p>This works in both directions. Leadership needs a clear account of the risk, while engineers need decisions that are precise enough to act on. “Please prioritise this” is not a useful decision if nobody has said what should move out of the way.</p>
<h2 id="my-job-has-two-parts-reactive-and-proactive">My job has two parts: reactive and proactive</h2>
<p>I tend to think of my job as having two parts. One is <strong>reactive</strong> - responding to problems as they happen. The other is <strong>proactive</strong> - helping teams prepare for what is coming before it becomes a problem.</p>
<p>An example of the reactive work might be a critical bug passed my way because it crosses team boundaries, needs more investigation, or requires the right people to be pulled in quickly. I might dig into the issue myself, help establish who owns it, or escalate it when the impact or urgency is not yet visible to the people who can act.</p>
<figure><img src="https://deanhume.com/content/posts/what-a-technical-program-manager-actually-does/reactive-proactive.jpg" alt="Technical Program Manager - Reactive &amp; Proactive" loading="lazy"></figure>
<p>The proactive side is quieter but just as important. I spend time educating developers and teams about upcoming features, useful new tools, and changes that could affect how they work. This could take the form of building developer communities, hosting monthly meetings or even larger multi-day Summits.</p>
<p>This could be anything from introducing people to something new that saves them time, to helping them prepare for an upcoming deprecation, or simply making sure they&#39;re aware that a tool or service is nearing the end of its supported lifecycle.</p>
<p>If this is done early, it gives teams time to understand the change and plan around it. If it&#39;s done late, the same change arrives as another urgent problem.</p>
<p>The two parts reinforce each other. Reactive work shows me where teams are missing context or where ownership is unclear. Proactive work lets me use that knowledge to prevent the same kind of surprise from happening again.</p>
<h2 id="the-technical-part-matters">The technical part matters</h2>
<p>The word “technical” is not decorative.</p>
<p>A Technical Program Manager does not need to be the strongest engineer in the room, but they do need <a href="https://deanhume.com/staying-technical-as-a-technical-program-manager">enough technical depth</a> to ask useful questions and understand why a proposed shortcut may create problems elsewhere.</p>
<p>Without that technical grounding, it is easy to reduce the work to progress reviews. The TPM becomes a messenger carrying updates between people who already understand the problem better.</p>
<p>With that context, I try to help connect the dots across teams. They can see how a technical decision in one area might create a schedule risk somewhere else, or recognise that what looks like a delivery issue is actually an ownership, architecture, or decision-making challenge.</p>
<p>The value is not knowing everything. It is being able to find the right detail and keep the programme-level consequence in view.</p>
<h2 id="so-what-does-a-tpm-actually-do">So, what does a TPM actually do?</h2>
<p>A TPM helps teams deliver complex technical work with fewer surprises. They connect the dots across teams, expose hidden dependencies, and help keep work moving.</p>
<p>Project plans and meetings are all tools for doing that but that is not the job itself. The <a href="https://deanhume.com/as-engineering-teams-get-smaller-is-the-technical-program-manager-the-missing-piece">exact shape of a Technical Program Managers&#39; role varies</a> enormously between companies - and sometimes between teams in the same company. If you are a TPM, Program Manager, Producer or Engineering Leader, I’d be interested to hear your thoughts!</p>]]></content:encoded></item><item><title><![CDATA[Why Short AI Coding Prompts Can Cost You More Time]]></title><description><![CDATA[Short AI coding prompts can create more back-and-forth. Give the assistant the problem, source material and constraints it needs to finish the task.]]></description><link>https://deanhume.com/why-short-ai-coding-prompts-can-cost-you-more-time/</link><guid isPermaLink="false">https://deanhume.com/why-short-ai-coding-prompts-can-cost-you-more-time/</guid><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Thu, 03 Sep 2026 09:17:56 GMT</pubDate><media:content url="https://deanhume.com/content/posts/why-short-ai-coding-prompts-can-cost-you-more-time/shorter-ai-coding-prompts-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/why-short-ai-coding-prompts-can-cost-you-more-time/shorter-ai-coding-prompts-1280.webp" alt="Why Short AI Coding Prompts Can Cost You More Time"><p>Ever tried to save time by keeping a prompt short - only to spend the next ten minutes answering follow-up questions because the AI didn&#39;t have what it needed?</p>
<p>Yeah, me too.</p>
<p>I recently came across a really interesting post from the <a href="https://github.blog/ai-and-ml/github-copilot/how-we-make-ai-coding-more-cost-efficient-without-sacrificing-task-quality/">GitHub Copilot team about making AI coding more cost efficient without sacrificing task quality</a>. One of the ideas that stuck with me was something they called the &quot;local metric trap.&quot;</p>
<p>The basic idea is simple: you optimise the bit you can see - one response or tool call - but accidentally make the whole task slower and more expensive.</p>
<p>GitHub found this while testing ways to compress tool output. When useful details were removed, the agent would reopen the original output or rerun the command to find them again. The first response was shorter, but all that recovery work ended up costing more.</p>
<p>GitHub was looking at the machinery inside its agent harness - not studying how developers write prompts. Weirdly, I recognised the same pattern in how I use coding assistants day to day.</p>
<p>Here are a few practical lessons I took from it:</p>
<p><strong>Give the assistant the problem, not just the label</strong></p>
<blockquote>&quot;Fix the bug in <code>payment.js</code>&quot;</blockquote>
<p>Feels nice and efficient, but it leaves most of the useful work for the next message.</p>
<p>Compare that with:</p>
<blockquote>&quot;<em>In <code>payment.js</code>, <code>processRefund()</code> throws <code>TypeError: Cannot read property &#39;amount&#39; of undefined</code> when a refund is issued for an order with no line items - here&#39;s the stack trace.</em>&quot;</blockquote>
<p>It takes a little longer to write, but at least the assistant has somewhere useful to start.</p>
<p><strong>Share the real source material when it matters</strong></p>
<p>I&#39;ll sometimes describe a function from memory instead of pasting the relevant code: &quot;<em>It takes a request, checks authentication, then saves to the database.</em>&quot; The problem is that my tidy little summary can leave out the exact branch or weird input that&#39;s causing the bug.</p>
<p>GitHub ran into a similar problem when it tried compressing <code>git diff</code> output. Agents kept reopening the original diff to recover details that had been removed, so GitHub stopped compressing that kind of source-like output. Sometimes the original really is the most efficient version.</p>
<p><strong>Be careful when tidying up your instructions files</strong></p>
<p>If you&#39;ve got a <code>CLAUDE.md</code>, <code>AGENTS.md</code>, or Copilot instructions file with hard rules in it - don&#39;t edit <code>/generated</code>, always run tests before committing - it&#39;s tempting to rewrite it so it sounds shorter and punchier.</p>
<p>That can quietly change the behaviour.</p>
<p>GitHub discovered this while shortening its own task-tool guidance. A rewrite accidentally turned flexible advice about parallel agents into a rigid policy, which made independent agents run one at a time. The team stopped the experiment, added a regression test, and eventually found a shorter instruction that kept the behaviour they wanted.</p>
<p>The important bit here is to test what the assistant actually does after changing an instruction - not just whether it can repeat the rule back to you.</p>
<p><strong>Give the full shape of a related task upfront</strong></p>
<p>If you want a pull request review to cover correctness, test coverage, and security, say that at the beginning. Splitting it into three separate requests can add more turns and carry the same working context through the conversation again.</p>
<p>That doesn&#39;t mean throwing every unrelated job into one enormous prompt. If the requests depend on the same files and context, batch them together. If they don&#39;t, keep them separate.</p>
<p>None of this means that longer prompts are automatically better. It&#39;s really about giving the assistant enough useful information to finish the job without having to retrace its steps.</p>
<p>So next time, give the AI what it needs upfront and save yourself the back-and-forth - I hope that helps🚀</p>]]></content:encoded></item><item><title><![CDATA[Why Readiness Should Be a Habit, Not a Final Gate]]></title><description><![CDATA[Quality isn't decided in the final review. The teams with the smoothest launches built it through daily habits, long before anyone thought about a release date.]]></description><link>https://deanhume.com/why-readiness-should-be-a-habit-not-a-final-gate/</link><guid isPermaLink="false">https://deanhume.com/why-readiness-should-be-a-habit-not-a-final-gate/</guid><category><![CDATA[Technical Leadership]]></category><category><![CDATA[Technical Program Manager]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Mon, 03 Aug 2026 08:41:00 GMT</pubDate><media:content url="https://deanhume.com/content/posts/why-readiness-should-be-a-habit-not-a-final-gate/photo-1580047750144-2c7790adf461-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/why-readiness-should-be-a-habit-not-a-final-gate/photo-1580047750144-2c7790adf461-1280.webp" alt="Why Readiness Should Be a Habit, Not a Final Gate"><p>When does quality actually get decided on your team?</p>
<p>Most people would say it&#39;s decided at the end - the final testing pass, the pre-release bug bash, the last sprint before launch.</p>
<p>I don&#39;t think that&#39;s true.</p>
<p>The teams I&#39;ve seen ship the smoothest launches had already decided their quality months earlier. Not through some grand initiative. Through a hundred small habits, built up long before anyone was thinking about a release date.</p>
<h2 id="the-problem-with-treating-quality-as-a-phase">The problem with treating quality as a phase</h2>
<p>Here&#39;s the pattern I keep seeing. A team builds a feature, it works, everyone moves on. Weeks later, something breaks it - a network drops, a user signs out mid-flow, a session gets interrupted - and suddenly there&#39;s a &quot;quality bug&quot; to fix before launch.</p>
<p>Except it usually isn&#39;t a bug. Call it that, and you&#39;re saying the behaviour was defined somewhere and someone just built it wrong. Most of the time, nobody defined it at all. What happens if a session drops mid-flow was never written down - it was just assumed, the same way everyone assumes a bridge won&#39;t collapse without anyone putting &quot;please don&#39;t collapse&quot; in the blueprint.</p>
<figure><img src="https://deanhume.com/content/posts/why-readiness-should-be-a-habit-not-a-final-gate/image-1.png" alt="Illustration representing software readiness and quality habits built into an engineering team&#39;s workflow" loading="lazy"></figure>
<p>That distinction matters more than it sounds. &quot;Fix this bug&quot; and &quot;we never actually decided what should happen here&quot; are two different conversations - and only one of them is a five-minute ticket. Calling it a bug is just the easier thing to say. It skips the harder admission: that this behaviour, whatever you want to call it, was never part of the spec in the first place.</p>
<p>That&#39;s the trap with treating quality as a phase instead of a habit. By the time you&#39;re looking for these problems, you&#39;re also under the most pressure to ship - exactly the wrong moment to be having a &quot;wait, was this ever actually defined?&quot; conversation. Every found issue becomes a fire, not a finding.</p>
<h2 id="think-in-systems-not-features">Think in systems, not features</h2>
<p>When you build a feature, it&#39;s natural to focus on what it&#39;s supposed to do. A login flow logs people in. A save function saves data. A sync service syncs.</p>
<p>But most of the interesting failures live outside that primary purpose. What happens if the network disconnects mid-save? If the laptop lid closes halfway through? If two systems try to sync the same data at once? If someone revokes an API token mid-request?</p>
<p>None of these are exotic. They&#39;re the ordinary reality of software running on real machines, on real networks, used by real people who don&#39;t behave the way your test script does.</p>
<p>The shift that helps: stop asking &quot;does this feature work?&quot; Start asking &quot;what happens around this feature when things go wrong?&quot; That one question surfaces more real issues than almost anything else I know.</p>
<h2 id="failure-handling-is-a-feature-not-an-afterthought">Failure handling is a feature, not an afterthought</h2>
<p>A user will never notice a save system that works perfectly. They will absolutely notice one that loses their progress.</p>
<p>The things that go right are invisible and the things that go wrong are the only things anyone remembers. Failure handling isn&#39;t a lesser cousin of the &quot;real&quot; feature - it&#39;s often the thing that decides whether people trust your product at all.</p>
<p>Building this in doesn&#39;t need to be dramatic. Add &quot;<em>what if this fails halfway through?</em>&quot; to your definition of done, right next to &quot;<em>does this meet the acceptance criteria?</em>&quot;</p>
<h2 id="quality-is-not-one-teams-job">Quality is not one team&#39;s job</h2>
<p>I get why quality often ends up owned by one group - a QA team, a dedicated tester, a release manager. Someone has to be accountable, and it&#39;s tidy to draw the line that way.</p>
<p>But the best outcomes come from readiness being shared, not delegated. Designers think about the journeys users actually take, not just the ones in the spec. Engineers think about what breaks under load. Ops thinks about what happens when a dependency goes down. Everyone brings a different failure mode to the table, and together they cover more ground than any single team could alone.</p>
<figure><img src="https://deanhume.com/content/posts/why-readiness-should-be-a-habit-not-a-final-gate/image.png" alt="The best outcomes come from readiness being shared, not delegated." loading="lazy"></figure>
<p>When quality is &quot;someone else&#39;s job,&quot; problems surface late - right before a deadline, when there&#39;s the least time to fix them properly. When it&#39;s everyone&#39;s habit, they surface early, while they&#39;re still cheap.</p>
<h2 id="this-only-works-if-you-have-a-big-team">&quot;This only works if you have a big team&quot;</h2>
<p>I know what some people are thinking - this sounds reasonable for a large, well-resourced org, but not realistic for a small team stretched thin across a dozen priorities.</p>
<p>Fair point, but these habits don&#39;t scale with headcount - they scale with attention. A team of three can still ask &quot;<em>what happens if this fails?</em>&quot; in code review. A team of three can still spend twenty minutes a sprint on a lightweight checklist instead of skipping it under pressure. The habit is cheap. It&#39;s the absence of it, compounding silently for months, that gets expensive.</p>
<h2 id="get-feedback-earlier-than-feels-comfortable">Get feedback earlier than feels comfortable</h2>
<p>One of the more counterintuitive things I&#39;ve learned is that teams get the most value from external feedback - a code review, a security scan, a beta test - when they ask for it earlier than feels natural. Most wait until they believe something is &quot;ready.&quot; By then, the feedback just confirms what you suspected, instead of reshaping your priorities while there&#39;s still time to act.</p>
<p>Whatever your version of a final review looks like, treat an early pass at it as a debugging tool, not a pass/fail exam. The earlier you know where the gaps are, the cheaper they are to close.</p>
<h2 id="a-lightweight-checklist-beats-no-checklist">A lightweight checklist beats no checklist</h2>
<p>Before anything goes out the door, it&#39;s worth running a short, boring checklist rather than trusting memory:</p>
<ol>
<li><strong>Deploy it cold.</strong> Install or deploy exactly the way it&#39;ll happen in production - on a clean machine or fresh container, not your dev box with months of workarounds baked in.</li>
<li><strong>Hand it to a stranger.</strong> Give it to someone with zero context and no instructions. If they can&#39;t reach the core experience without you standing over their shoulder, that&#39;s a real finding, not an edge case.</li>
<li><strong>Check the paper trail.</strong> Make sure the version number, changelog, and docs describe what&#39;s actually shipping - not what shipped last time.</li>
<li><strong>Retest, don&#39;t just re-close.</strong> Go back to previously reported bugs and confirm they&#39;re actually fixed - and add a regression test for each one, so it can&#39;t quietly come back. A ticket marked &quot;resolved&quot; isn&#39;t the same as a bug that&#39;s gone. Using AI agents for this is a perfect example of how to reduce churn.</li>
</ol>
<p>None of this is glamorous and all of it prevents avoidable churn later.</p>
<h2 id="the-real-takeaway">The real takeaway</h2>
<p>Over the years, I&#39;ve learned that the teams who dread their final review are usually the ones who&#39;ve been avoiding the hard questions the whole way through. The teams who walk in relaxed aren&#39;t lucky - they&#39;ve just already asked &quot;<em>what happens when this breaks?</em>&quot; a hundred times before anyone official asked it for them.</p>
<p>That&#39;s really the whole idea. The final check was never where quality got decided. It was just where you found out.</p>
<p>So next time you&#39;re reviewing a feature, don&#39;t just ask if it works. Ask what happens when it doesn&#39;t. Do that enough, and your &quot;final&quot; check stops being something you might fail - and starts being a formality that tells you what you already knew. 🙂</p>]]></content:encoded></item><item><title><![CDATA[A Maturity Matrix for Game Development]]></title><description><![CDATA[Use a practical maturity matrix to assess your game development team, spot gaps in engineering and people practices, and choose what to improve first.]]></description><link>https://deanhume.com/a-maturity-matrix-for-game-development/</link><guid isPermaLink="false">https://deanhume.com/a-maturity-matrix-for-game-development/</guid><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Tue, 21 Jul 2026 09:21:57 GMT</pubDate><media:content url="https://deanhume.com/content/posts/a-maturity-matrix-for-game-development/photo-1562229125-6d6075419a22-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/a-maturity-matrix-for-game-development/photo-1562229125-6d6075419a22-1280.webp" alt="A Maturity Matrix for Game Development"><p>A few years ago, I wrote about using a <a href="https://deanhume.com/software-team-maturity-matrix/">Maturity Matrix</a> to assess software engineering teams. The idea was simple - what does a great team look like, and how honestly can you measure yourself against that picture?</p>
<p>Since then, I&#39;ve moved into the gaming industry. Anyone who has taken the leap from traditional software development and into game development knows how different it is. Reusing the old matrix just wouldn&#39;t work.</p>
<p>I know what some of you are already thinking - games are creative work, not an assembly line, and a maturity matrix sounds like corporate process creeping into art. I&#39;d have said the same thing a couple of years ago. But the crunch numbers, the missed cert dates, and the launch-week crashes tell a different story. Creativity doesn&#39;t suffer because a team has good practices. It suffers when nobody has time to be creative because they&#39;re firefighting.</p>
<figure><img src="https://deanhume.com/content/posts/a-maturity-matrix-for-game-development/image-1.png" alt="Game development lifecycle from concept through release" loading="lazy"></figure>
<p>So here&#39;s the matrix I actually use now - rebuilt from scratch, with the lens I wish I&#39;d had on day one.</p>
<ul>
<li><em> </em></li>
</ul>
<h2 id="what-is-a-maturity-matrix">What is a Maturity Matrix?</h2>
<p>If you&#39;re new to the concept - a Maturity Matrix is a list of best practices, and you rate yourself honestly against each one. I like to keep it simple with a Red / Amber / Green system:</p>
<ul>
<li>🔴 <strong>Red</strong> - this doesn&#39;t exist, or we barely think about it</li>
<li>🟡 <strong>Amber</strong> - we do this sometimes, or it&#39;s a work in progress</li>
<li>🟢 <strong>Green</strong> - this is something we actively do and do well</li>
</ul>
<p>The point isn&#39;t to feel bad about the reds. It&#39;s to give you a clear, honest picture of where to focus your energy. A lot of reds in one area? That&#39;s your priority list right there.</p>
<p>I&#39;ve trimmed this down to the nine that matter most, in my experience - the ones that separate studios that ship well from studios that ship exhausted.</p>
<ul>
<li><em> </em></li>
</ul>
<h2 id="engineering-practices">Engineering Practices</h2>
<p><strong>Build Pipeline Health</strong></p>
<p>In web development, a slow build is annoying. In game development, a broken build can block an entire team - artists, designers, QA, and engineers all depend on it. I once watched an entire studio lose a full day because nobody could get a playable build out, three days before a milestone review. Are your builds automated and reliable? Are you catching console certification failures early, or discovering them at submission time? A healthy build pipeline is the backbone of a productive studio.</p>
<p><strong>Performance Profiling Culture</strong></p>
<p>Every platform has hard limits - CPU budgets, GPU budgets, memory ceilings. Does your team profile regularly, or only when something starts to hitch two weeks before launch? The best studios treat performance as a first-class feature from the start, not an afterthought you bolt on once the fun is figured out.</p>
<p><strong>Crash Reporting &amp; Telemetry</strong></p>
<p>When a live game crashes, your players notice before you do. Do you have crash reporting in place? Are you tracking telemetry on key gameplay events? Can you identify a root cause quickly, or are you hunting through logs hoping for a clue while your review score drops in real time?</p>
<p>Oh and if you are interested, I wrote an article on the XBOX Game dev blog entitled <a href="https://developer.microsoft.com/en-us/games/articles/2025/11/from-crash-to-resolution-practical-guide-for-xbox-windows-developers/">From Crash to Resolution: A Practical Guide for Xbox &amp; Windows Developers</a> about this very topic.</p>
<ul>
<li><em> </em></li>
</ul>
<h2 id="game-dev-specific-practices">Game Dev-Specific Practices</h2>
<p>These don&#39;t really come up on a traditional software team, but they&#39;re make-or-break in game development.</p>
<p><strong>Content Pipeline Maturity</strong></p>
<p>This is the game dev equivalent of CI/CD, and it&#39;s often overlooked. How quickly can an artist iterate on a texture or a level? How long before a designer&#39;s change shows up in a playable build? Slow content pipelines kill creativity and momentum quietly - nobody files a ticket for &quot;iteration felt sluggish,&quot; but it shows up in the work. A fast feedback loop is imperative to game dev.</p>
<p><strong>Platform Compliance Readiness</strong></p>
<p>Shipping on console means going through certification, and failing it costs real time and real money. Are you running compliance checks throughout development, or only at submission? The studios that handle this well treat certification like a test suite - run it early, run it often, don&#39;t let it be a surprise.</p>
<p><strong>Live Operations Readiness</strong></p>
<p>If you&#39;re running a live service game, this one&#39;s non-negotiable. Do you have feature flags that let you toggle things without a full patch? Can you push a hotfix quickly without breaking everything else? Do you have a rollback plan, or is &quot;cross our fingers&quot; the plan? Live ops is a discipline in itself, and teams that treat it as an afterthought pay for it at 2am.</p>
<p><strong>Milestone Discipline</strong></p>
<p>Alpha, Beta, Gold (or similar) - do these mean something concrete in your studio, or are they just dates on a calendar? A mature team has clearly defined milestone criteria. Everyone knows what &quot;alpha&quot; means, and everyone knows what still needs to happen before &quot;gold.&quot; Vague milestones are how you back into crunch without ever deciding to.</p>
<ul>
<li><em> </em></li>
</ul>
<h2 id="people-practices">People Practices</h2>
<p>This is the category I think studios most underinvest in. Technical excellence matters - but so does the health of the people doing the work.</p>
<p><strong>Sustainable Pace &amp; Crunch Awareness</strong></p>
<p>Crunch is the elephant in the room in game development. Does your team track hours? Is crunch treated as a signal that planning went wrong, or just part of the job - the cost of doing business? The studios with the best long-term output have learned to treat unsustainable pace as a problem to solve, not a badge of honour to hand out at the wrap party.</p>
<p><strong>Cross-Discipline Collaboration</strong></p>
<p>Games are built by engineers, artists, designers, and audio teams, often working on the same systems at the same time. How well do those handoffs work? Are your tools built with non-engineers in mind, or do they assume everyone can read a stack trace? The friction between disciplines is often where time quietly disappears.</p>
<ul>
<li><em> </em></li>
</ul>
<h2 id="how-to-use-this">How to Use This</h2>
<p>Grab a notebook or a spreadsheet and rate yourself honestly against each of these. Don&#39;t try to fix everything at once - pick your top two or three reds and make a plan around those first.</p>
<figure><img src="https://deanhume.com/content/posts/a-maturity-matrix-for-game-development/image.png" alt="Game Development - Maturity Matrix" loading="lazy"></figure>
<p>If you&#39;d like to make a copy of this spreadsheet for yourself, please head over to <a href="https://docs.google.com/spreadsheets/d/1aPHxNbG9vSEdGD4stmVRE9CJBM6DMK5bRmBEzG6uf-I/edit?usp=sharing">this link</a>.</p>
<p>The goal isn&#39;t perfection. It&#39;s awareness. You can&#39;t improve what you don&#39;t measure.</p>
<figure><img src="https://deanhume.com/content/posts/a-maturity-matrix-for-game-development/image-3.png" alt="Game Development Maturity Matrix" loading="lazy"></figure>
<p>The point of this exercise is to have better data and context on how your team works, and what might be their needs. It&#39;s also worth mentioning that the list above is a light place to start, you can definitely expand and add more categories!</p>
<p>If you&#39;ve done this kind of exercise with a software team before, some of these patterns will feel familiar. Teams get good at the things they talk about, and they neglect the things they don&#39;t. This matrix is just a way of making sure you&#39;re talking about the right things - before your players start talking about them for you. 🎮</p>]]></content:encoded></item><item><title><![CDATA[As Engineering Teams get smaller, is the Technical Program Manager the missing piece?]]></title><description><![CDATA[As AI shrinks engineering teams and the EM role splits in two, the Technical Program Manager could be the most overlooked answer hiding in plain sight.]]></description><link>https://deanhume.com/as-engineering-teams-get-smaller-is-the-technical-program-manager-the-missing-piece/</link><guid isPermaLink="false">https://deanhume.com/as-engineering-teams-get-smaller-is-the-technical-program-manager-the-missing-piece/</guid><category><![CDATA[Technical Leadership]]></category><category><![CDATA[Technical Program Manager]]></category><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Mon, 29 Jun 2026 10:27:25 GMT</pubDate><media:content url="https://deanhume.com/content/posts/as-engineering-teams-get-smaller-is-the-technical-program-manager-the-missing-piece/photo-1748283373393-efe3e2aa8d09-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/as-engineering-teams-get-smaller-is-the-technical-program-manager-the-missing-piece/photo-1748283373393-efe3e2aa8d09-1280.webp" alt="As Engineering Teams get smaller, is the Technical Program Manager the missing piece?"><p>Do you remember what a &quot;normal&quot; software team looked like?</p>
<p>A product manager, an engineering manager, a designer, and six or seven engineers. Weekly planning meetings, a backlog to groom, a sprint to run. For most of the last decade, that was just... how teams worked. It was the water we all swam in.</p>
<p>That was my daily life as an software engineer and then an engineering manager for most of my career. That shape was so familiar it felt like a law of nature rather than a design choice.</p>
<p>I recently came across an article on the <a href="https://leaddev.com/career-development/the-engineering-manager-role-is-splitting-in-two">LeadDev website</a> that argued that traditional team shape is breaking apart - and fast. AI is reshaping the traditional team structure that engineering managers are used to, and the engineering manager (EM) role itself is splitting into two very different futures. One stays close to the code. The other expands outward across multiple teams, with ratios that would have seemed absurd five years ago.</p>
<p>It&#39;s a well-argued piece and I agree with most of it. But reading it, I kept thinking: there&#39;s a third role sitting right in the middle of this split that barely gets a mention.</p>
<p>The <a href="https://deanhume.com/tag/leadership/">Technical Program Manager</a> (aka a TPM).</p>
<p><strong>I&#39;ve spent time on both sides of the engineering/management divide.</strong> I&#39;ve been an engineering manager, and I&#39;m an IC now. And one thing that experience teaches you is that the messiest, most overlooked part of any org isn&#39;t the people management or the code - it&#39;s the space between teams. The dependencies. The cross-functional delivery risks. The &quot;<em>who owns this?</em>&quot; conversations that stall projects for weeks.</p>
<p>That&#39;s exactly the space that gets wider as teams shrink.</p>
<p>I know what you&#39;re thinking - aren&#39;t we just adding extra headcount? Not quite. A single TPM can serve across four or five of those small teams simultaneously, handling the cross-cutting work that would otherwise fragment across every Engineering Manager and tech lead in the building. It&#39;s not a new layer. It&#39;s a better shape.</p>
<p>That LeadDev article is really describing one thing: small, fast-moving, AI-augmented teams. Tech leads that are head down in the code. Wide-span engineering managers that are spread thin across five or six of those teams. Who&#39;s looking at the <strong>whole</strong> picture? Who&#39;s tracking that Team A&#39;s output is the input for Team B&#39;s Q3 milestone? Who&#39;s in the room when the product roadmap and the engineering capacity don&#39;t line up?</p>
<p>That work doesn&#39;t disappear just because management layers do. If anything, it gets harder.</p>
<p>A good TPM doesn&#39;t typically manage teams and doesn&#39;t write production code - but they hold the connective tissue of a programme together. They ask the questions that fall between job descriptions. They translate between engineering constraints and business timelines. They&#39;re technical enough to smell risk early and organised enough to do something about it.</p>
<p>In a world where your Engineering Manager is managing multiple engineers across multiple teams, the last thing they have time for is running a cross-team dependency log or facilitating a technical risk review. That&#39;s not a criticism - it&#39;s just scope. Something has to give.</p>
<p>The Technical Program Manager (TPM) fills that gap.</p>
<p>So why isn&#39;t this already the obvious answer? Partly because the TPM role itself is poorly understood. Ask ten people what a TPM does and you&#39;ll get ten different answers - some companies use the title for someone close to project management, others for a semi-technical lead, others barely use it at all. It&#39;s a role that&#39;s been historically inconsistent, which makes it easy to overlook when org charts get redrawn. But that inconsistency is also an opportunity - as the Engineering Manager role fractures, companies will need to define this connective-tissue work somehow, and the TPM title is sitting right there, half-formed and ready to be shaped into something more central.</p>
<p><strong>What&#39;s interesting is that the skills which make a great TPM are almost the opposite of what each fork of the Engineering Manager role is optimising for.</strong> The Tech Lead path doubles down on technical depth. The wide-span Engineering Manager path doubles down on people skills and influence. The Technical Program Manager has to hold both - enough technical credibility to challenge engineering estimates, enough interpersonal range to keep a dozen stakeholders aligned.</p>
<p>If you&#39;re currently an Engineering Manager wondering which way to lean as your org flattens, it might be worth asking yourself: which part of the job do you actually find energising? The 1:1s and the career conversations, or the delivery and the cross-team puzzles? Because that second category - that&#39;s Technical Program Manager territory, and it might be a better fit than either fork.</p>
<p>And if you&#39;re an IC watching all of this from the sidelines, wondering where the growth paths go from here - don&#39;t sleep on it. The role is less visible than Engineering Manager and less glamorous than staff engineer, but in a flatter org running lots of small teams, it might just be the most useful person in the building. 🙂</p>]]></content:encoded></item><item><title><![CDATA[Meeting Notes - A Free Desktop App for Tracking 1:1s]]></title><description><![CDATA[Meeting Notes is a free offline desktop app for 1:1s. Organise notes by person, tag topics, and transcribe meetings on-device with Whisper.]]></description><link>https://deanhume.com/meeting-notes-desktop-app-windows/</link><guid isPermaLink="false">https://deanhume.com/meeting-notes-desktop-app-windows/</guid><category><![CDATA[Technical Leadership]]></category><category><![CDATA[Technical Program Manager]]></category><category><![CDATA[Writing]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Mon, 29 Jun 2026 10:16:48 GMT</pubDate><media:content url="https://deanhume.com/content/posts/meeting-notes-desktop-app-windows/meeting_notes_hero-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/meeting-notes-desktop-app-windows/meeting_notes_hero-1280.webp" alt="Meeting Notes - A Free Desktop App for Tracking 1:1s"><p>Have you ever walked out of a 1:1 and realised you can&#39;t quite remember what you agreed on last time? Or spent five minutes before a catch-up scrolling through Slack threads, trying to piece together a conversation from three weeks ago?</p>
<p>I&#39;ve been there more times than I&#39;d like to admit. That&#39;s exactly why I built <a href="https://deanhume.github.io/meeting-notes/">Meeting Notes</a> - a lightweight, local-first desktop app for keeping organised notes with the people you work with.</p>
<figure><img src="https://deanhume.com/content/posts/meeting-notes-desktop-app-windows/image-12.png" alt="Meeting Notes - Free Offline Desktop App for 1:1s" loading="lazy"></figure>
<h2 id="why-i-built-it">Why I built it</h2>
<p>Most of us are using the wrong tool for this. Notion is powerful but noisy. Word and Google Docs works, but it&#39;s not really shaped for quick, personal notes. And anything cloud-based means your candid thoughts about a tricky conversation are living on someone else&#39;s server.</p>
<p>I wanted something simple and fast. Something that felt less like a productivity system and more like a trusted notebook. No accounts to create, no syncing to worry about, no cloud. Just notes, stored on your machine.</p>
<h2 id="what-it-does">What it does</h2>
<p>The app is organised around people, not documents. You add the folks you meet with regularly - their name, role, and team - and every note you take lives under their profile. It builds up a natural history of every conversation over time.</p>
<p>Here&#39;s what&#39;s included:</p>
<ul>
<li>📝 Track meeting notes for multiple people</li>
<li>👥 Manage contacts with names, roles, and teams</li>
<li>🏷️ Tag and filter notes by topic (things like <code>hiring</code>, <code>architecture</code>, or <code>follow-up</code>)</li>
<li>✍️ Markdown support with live preview and a formatting toolbar</li>
<li>🎙️ Voice recording with offline speech-to-text - record your mic and/or system audio and transcribe straight into the note</li>
<li>💾 Autosave every 20 keystrokes, so you never lose your work</li>
<li>🎨 Light/dark theme</li>
<li>💡 Discussion prompts suggested when you start a new note</li>
</ul>
<p>And everything runs completely offline. Your data never leaves your machine.</p>
<h2 id="the-voice-transcription-feature">The voice transcription feature</h2>
<p>This is the one I&#39;m most pleased with. Hit record, have your conversation, and the transcript drops straight into your note - all on-device using the Whisper model. No audio is ever sent anywhere. It even summarises what you discussed. I use it during walking 1:1s and it&#39;s become one of those features I didn&#39;t know I needed until I had it.</p>
<figure><img src="https://deanhume.com/content/posts/meeting-notes-desktop-app-windows/image-11.png" alt="Meeting Notes desktop app - 1:1 note tracking for Windows" loading="lazy"></figure>
<p>Meeting Notes Desktop App lets you record and summarise using AI</p>
<h2 id="give-it-a-try">Give it a try</h2>
<p>Meeting Notes is free, open source (MIT), and available now for Windows. Head over to the <a href="https://github.com/deanhume/meeting-notes/releases">releases page</a> to grab the latest version - just run the installer and you&#39;re up and running in under a minute.</p>
<p>👉 <a href="https://github.com/deanhume/meeting-notes/releases">**Download Meeting Notes**</a> - v1.2.0 · Windows x64</p>
<p>The <a href="https://github.com/deanhume/meeting-notes">source code is on GitHub</a> too, if you want to take a look, contribute, or build it yourself.</p>
<p>If you&#39;ve got any thoughts or run into anything unexpected, I&#39;d love to hear from you - drop a comment or open an issue. Happy note taking! 🚀</p>]]></content:encoded></item><item><title><![CDATA[Building a Custom MCP Server with Node.js]]></title><description><![CDATA[Build a Node.js MCP server that answers bin collection questions from a JSON file, then connect it to VS Code so your AI assistant can use your data.]]></description><link>https://deanhume.com/building-a-custom-mcp-server-with-node-js/</link><guid isPermaLink="false">https://deanhume.com/building-a-custom-mcp-server-with-node-js/</guid><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Thu, 04 Jun 2026 10:01:24 GMT</pubDate><media:content url="https://deanhume.com/content/posts/building-a-custom-mcp-server-with-node-js/photo-1667372335936-3dc4ff716017-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/building-a-custom-mcp-server-with-node-js/photo-1667372335936-3dc4ff716017-1280.webp" alt="Building a Custom MCP Server with Node.js"><p>Ever wished your AI assistant actually knew <em>your</em> stuff? Your files, your schedule, or even a custom data source? That&#39;s exactly what the <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a> (MCP) makes possible.</p>
<p>MCP is an open standard for connecting AI assistants like Claude to external data sources and tools. Think of it like a USB port for AI - a single, standardised connector that works with any compatible device. Build the server once, plug it into any MCP-compatible client.</p>
<p>In this guide, we&#39;ll build a custom MCP server from scratch using Node.js. The example is deliberately practical: a bin collection reminder system. Simple enough to grasp quickly. Complex enough to cover every core concept you&#39;ll need.</p>
<h2 id="what-is-the-model-context-protocol">What is the Model Context Protocol?</h2>
<p>MCP gives AI assistants a common language for talking to the outside world. Instead of every tool needing its own bespoke integration, MCP provides a shared standard.</p>
<p>Your MCP server exposes &quot;tools&quot; - functions that AI Assistants can call. The server does the work (reading files, querying a database, calling an API), then returns structured data. The AI Assistant handles the rest.</p>
<p>Right now, Claude Desktop and VS Code (amongst others) support MCP out of the box. The ecosystem is growing fast.</p>
<h2 id="the-problem-were-solving">The Problem We&#39;re Solving</h2>
<p>To build a basic example (and experiment with real data in my life) - I wanted to create an MCP server that gave me the details of my Bin Collections. I wanted to be able to run a simple chat query and get a response. Ideally, this would save me time logging onto my local council&#39;s website and entering my information time and time again.</p>
<p>Here&#39;s the scenario. Garden waste goes out fortnightly on Fridays. Recycling is every Saturday. General refuse is also fortnightly on Fridays - but on different weeks. It&#39;s surprisingly easy to get wrong, especially when you factor in that holidays throw the schedule off.</p>
<p>So we&#39;ll build an MCP server that Claude can query to answer questions like &quot;when are my bins next due out?&quot; A simple use case that&#39;s genuinely useful and it teaches you the full pattern.</p>
<h2 id="setting-up-the-project">Setting Up the Project</h2>
<p>Create a new Node.js project:</p>
<pre><code class="language-bash">mkdir bin-collection-mcp
cd bin-collection-mcp
npm init -y</code></pre>
<p>You only need one dependency - the official MCP SDK:</p>
<pre><code class="language-bash">npm install @modelcontextprotocol/sdk</code></pre>
<p>Update your <code>package.json</code> to use ES modules. Add <code>&quot;type&quot;: &quot;module&quot;</code>:</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;bin-collection-mcp&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;MCP server for local bin collection day lookups&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;dependencies&quot;: {
    &quot;@modelcontextprotocol/sdk&quot;: &quot;^1.0.0&quot;
  }
}</code></pre>
<h2 id="the-data-structure">The Data Structure</h2>
<p>Before writing any server code, think about your data. We&#39;ll use a simple JSON file - <code>bin-days.json</code> - that stores collection dates:</p>
<pre><code class="language-json">{
  &quot;collections&quot;: [
    {
      &quot;date&quot;: &quot;2026-05-29&quot;,
      &quot;day&quot;: &quot;Friday&quot;,
      &quot;types&quot;: [&quot;garden&quot;],
      &quot;notes&quot;: &quot;&quot;
    },
    {
      &quot;date&quot;: &quot;2026-05-30&quot;,
      &quot;day&quot;: &quot;Saturday&quot;,
      &quot;types&quot;: [&quot;recycling&quot;],
      &quot;notes&quot;: &quot;&quot;
    }
  ],
  &quot;binTypes&quot;: {
    &quot;general&quot;: {
      &quot;colour&quot;: &quot;black&quot;,
      &quot;description&quot;: &quot;General household refuse&quot;
    },
    &quot;garden&quot;: {
      &quot;colour&quot;: &quot;brown&quot;,
      &quot;description&quot;: &quot;Garden waste - grass, leaves, small branches&quot;
    },
    &quot;recycling&quot;: {
      &quot;colour&quot;: &quot;blue&quot;,
      &quot;description&quot;: &quot;Paper, card, plastics, tins, glass&quot;
    }
  }
}</code></pre>
<p>Each collection has a date, day name, an array of bin types, and optional notes. The <code>binTypes</code> object describes what each bin is for. Keep it readable - you&#39;ll be updating this file manually.</p>
<h2 id="building-the-mcp-server">Building the MCP Server</h2>
<p>Create <code>index.js</code>. We&#39;ll build it in steps.</p>
<h3 id="step-1-import-dependencies-and-load-data">Step 1 - Import Dependencies and Load Data</h3>
<pre><code class="language-javascript">#!/usr/bin/env node

import { Server } from &quot;@modelcontextprotocol/sdk/server/index.js&quot;;
import { StdioServerTransport } from &quot;@modelcontextprotocol/sdk/server/stdio.js&quot;;
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from &quot;@modelcontextprotocol/sdk/types.js&quot;;
import { readFileSync } from &quot;fs&quot;;
import { resolve, dirname } from &quot;path&quot;;
import { fileURLToPath } from &quot;url&quot;;

const __dirname = dirname(fileURLToPath(import.meta.url));
const BIN_DAYS_PATH = resolve(__dirname, &quot;bin-days.json&quot;);

function loadBinDays() {
  const raw = readFileSync(BIN_DAYS_PATH, &quot;utf-8&quot;);
  return JSON.parse(raw);
}</code></pre>
<p>The shebang at the top makes the file directly executable. We&#39;re loading the JSON synchronously each time it&#39;s needed. Not the most efficient approach - but simple, and it means changes to your data file are picked up immediately.</p>
<h3 id="step-2-add-helper-functions">Step 2 - Add Helper Functions</h3>
<p>Here&#39;s the business logic before we wire up the server:</p>
<pre><code class="language-javascript">function getUpcomingCollections(data, daysAhead = 30) {
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const cutoff = new Date(today);
  cutoff.setDate(cutoff.getDate() + daysAhead);

  return data.collections.filter((c) =&gt; {
    const date = new Date(c.date);
    return date &gt;= today &amp;&amp; date &lt;= cutoff;
  });
}

function getNextCollection(data, binType) {
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const upcoming = data.collections
    .filter((c) =&gt; {
      const date = new Date(c.date);
      if (date &lt; today) return false;
      if (binType) return c.types.includes(binType.toLowerCase());
      return true;
    })
    .sort((a, b) =&gt; new Date(a.date) - new Date(b.date));

  return upcoming[0] || null;
}</code></pre>
<p><code>getUpcomingCollections</code> returns everything within a date window. <code>getNextCollection</code> finds the soonest one, with an optional filter by bin type. Clean, testable functions. Keep your business logic separate from your server wiring.</p>
<h3 id="step-3-initialise-the-server">Step 3 - Initialise the Server</h3>
<pre><code class="language-javascript">function createServer() {
  const server = new Server(
    { name: &quot;bin-collection&quot;, version: &quot;1.0.0&quot; },
    { capabilities: { tools: {} } }
  );
}</code></pre>
<p>The <code>capabilities</code> object tells clients what your server can do. Here we&#39;re saying: this server exposes tools. That&#39;s all your AI assistant needs to know upfront.</p>
<h3 id="step-4-define-your-tools">Step 4 - Define Your Tools</h3>
<p>This is where MCP gets interesting. You&#39;re essentially writing a contract - here&#39;s what I can do, here&#39;s what you can ask me:</p>
<pre><code class="language-javascript">server.setRequestHandler(ListToolsRequestSchema, async () =&gt; ({
    tools: [
      {
        name: &quot;get_upcoming_collections&quot;,
        description:
          &quot;Returns all bin collections scheduled in the next N days (default 30). Useful for planning ahead or giving a full schedule overview.&quot;,
        inputSchema: {
          type: &quot;object&quot;,
          properties: {
            days_ahead: {
              type: &quot;number&quot;,
              description:
                &quot;How many days ahead to look for collections (default: 30)&quot;,
            },
          },
        },
      },
      {
        name: &quot;get_next_collection&quot;,
        description:
          &quot;Returns the next upcoming bin collection, optionally filtered by bin type (general, recycling, garden).&quot;,
        inputSchema: {
          type: &quot;object&quot;,
          properties: {
            bin_type: {
              type: &quot;string&quot;,
              description:
                &quot;Filter by bin type: &#39;general&#39;, &#39;recycling&#39;, or &#39;garden&#39;. Leave empty for any type.&quot;,
              enum: [&quot;general&quot;, &quot;recycling&quot;, &quot;garden&quot;],
            },
          },
        },
      },
      {
        name: &quot;get_bin_types&quot;,
        description:
          &quot;Returns details about each bin type - colour and what can be put in it.&quot;,
        inputSchema: {
          type: &quot;object&quot;,
          properties: {},
        },
      },
      },
    ],
  }));</code></pre>
<p>Your <code>inputSchema</code> uses JSON Schema to define parameters. The AI assistant reads this to understand what arguments to pass. Notice the <code>enum</code> on <code>bin_type</code> - that tells the AI assistant exactly which values are valid. Good schemas mean fewer errors.</p>
<h3 id="step-5-handle-tool-calls">Step 5 - Handle Tool Calls</h3>
<p>When the AI assistant actually calls one of your tools, this handler runs:</p>
<pre><code class="language-javascript">server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
    const { name, arguments: args } = request.params;
    const data = loadBinDays();

    if (name === &quot;get_upcoming_collections&quot;) {
      const daysAhead = args?.days_ahead ?? 30;
      const collections = getUpcomingCollections(data, daysAhead);

      if (collections.length === 0) {
        return {
          content: [
            {
              type: &quot;text&quot;,
              text: `No bin collections found in the next ${daysAhead} days.`,
            },
          ],
        };
      }

      const formatted = collections
        .map((c) =&gt; {
          const types = c.types.join(&quot;, &quot;);
          const note = c.notes ? ` (${c.notes})` : &quot;&quot;;
          return `• ${c.date} (${c.day}): ${types}${note}`;
        })
        .join(&quot;\n&quot;);

      return {
        content: [
          {
            type: &quot;text&quot;,
            text: `Upcoming bin collections (next ${daysAhead} days):\n\n${formatted}`,
          },
        ],
      };
    }

    if (name === &quot;get_next_collection&quot;) {
      const binType = args?.bin_type || null;
      const next = getNextCollection(data, binType);

      if (!next) {
        const label = binType ? `${binType} bin` : &quot;any bin&quot;;
        return {
          content: [
            {
              type: &quot;text&quot;,
              text: `No upcoming collections found for ${label}.`,
            },
          ],
        };
      }

      const types = next.types.join(&quot;, &quot;);
      const note = next.notes ? `\nNote: ${next.notes}` : &quot;&quot;;
      const label = binType ? `Next ${binType} collection` : &quot;Next collection&quot;;

      return {
        content: [
          {
            type: &quot;text&quot;,
            text: `${label}: ${next.date} (${next.day})\nBins out: ${types}${note}`,
          },
        ],
      };
    }

    if (name === &quot;get_bin_types&quot;) {
      const lines = Object.entries(data.binTypes)
        .map(
          ([key, val]) =&gt;
            `• ${key} (${val.colour} bin): ${val.description}`
        )
        .join(&quot;\n&quot;);

      return {
        content: [
          {
            type: &quot;text&quot;,
            text: `Bin types:\n\n${lines}`,
          },
        ],
      };
    }</code></pre>
<p>Each tool returns a <code>content</code> array with text. You can return images or embedded resources too - but text handles most cases just fine.</p>
<h3 id="step-6-start-the-server">Step 6 - Start the Server</h3>
<pre><code class="language-javascript">const transport = new StdioServerTransport();
await server.connect(transport);
console.error(&quot;Bin collection MCP server running&quot;);</code></pre>
<p>We&#39;re using <code>stdio</code> transport - the server talks via standard input/output. That&#39;s why logging goes to <code>console.error</code>. Stdout is reserved for MCP messages.</p>
<h2 id="testing-it">Testing It</h2>
<p>Make the file executable, then run it:</p>
<pre><code class="language-bash">chmod +x index.js
node index.js</code></pre>
<p>You should see &quot;Bin collection MCP server running&quot; in the console. The server is now waiting for requests on stdin. You can also write unit tests against your helper functions directly - they&#39;re just plain JavaScript functions.</p>
<h2 id="connecting-to-visual-studio-code">Connecting to Visual Studio Code</h2>
<p>VS Code supports MCP servers through its settings. Open your <code>settings.json</code> (Cmd+Shift+P → &quot;Open User Settings JSON&quot;) and add the following:</p>
<pre><code class="language-json">{
  &quot;mcp&quot;: {
    &quot;servers&quot;: {
      &quot;bin-collection&quot;: {
        &quot;type&quot;: &quot;stdio&quot;,
        &quot;command&quot;: &quot;node&quot;,
        &quot;args&quot;: [
          &quot;/absolute/path/to/bin-collection-mcp/index.js&quot;
        ]
      }
    }
  }
}</code></pre>
<p>Reload VS Code and your server should be picked up automatically. Then try asking Copilot Chat in agent mode:</p>
<ul>
<li>&quot;When are my bins next due out?&quot;</li>
<li>&quot;Show me all collections in the next 7 days&quot;</li>
<li>&quot;When is the next recycling collection?&quot;</li>
</ul>
<p>VS Code calls your tools automatically and gives you a natural language answer. It feels like magic the first time. ✨</p>
<h2 id="the-pattern-to-remember">The Pattern to Remember</h2>
<p>MCP servers always follow the same structure. List your tools. Handle tool calls. Return content. That&#39;s it. Once you&#39;ve got this pattern in your head, you can build servers for anything - your local filesystem, an internal API, a Raspberry Pi sensor, whatever you like.</p>
<p>I took this a step further and deployed this code to an Azure App Service and this made it available from anywhere. The full code for this project is on <a href="https://github.com/deanhume/bin-days-mcp">GitHub</a>. Give it a try - and if you build something interesting with it, I&#39;d love to hear about it.</p>]]></content:encoded></item><item><title><![CDATA[Technical Skills Every Technical Program Manager Should Learn in 2026]]></title><description><![CDATA[The technical skills every TPM needs in 2026 from cloud basics to AI agents and governance. A practical guide to staying close to the tech.]]></description><link>https://deanhume.com/technical-skills-every-tpm-should-learn-in-2026/</link><guid isPermaLink="false">https://deanhume.com/technical-skills-every-tpm-should-learn-in-2026/</guid><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Tue, 05 May 2026 14:12:37 GMT</pubDate><media:content url="https://deanhume.com/content/posts/technical-skills-every-tpm-should-learn-in-2026/technical-program-manager-skills-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/technical-skills-every-tpm-should-learn-in-2026/technical-program-manager-skills-1280.webp" alt="Technical Skills Every Technical Program Manager Should Learn in 2026"><p>Over the years, I’ve learned that being a Technical Program Manager (TPM) is a bit of a strange hybrid role. You’re expected to understand the tech well enough to contribute meaningfully, but you’re not the one writing and deploying the code anymore. The best TPMs I’ve worked with are the ones who stay close to the technology - curious enough to dig in, and technical enough to be useful.</p>
<p>The challenge? Tech keeps changing. Super Fast. And in 2026, AI has pretty much woven itself into every part of engineering. Systems are more distributed, products ship faster, and teams expect you to keep up.</p>
<p>A while back, I wrote about <a href="https://deanhume.com/staying-technical-as-a-technical-program-manager/">how to stay technical as a TPM</a> - the habits, the routines, the mindset. Tinkering with side projects, spinning up local environments, sitting in on design reviews.</p>
<p>Building on from that article, here are the technical skills I’ve found most useful as a TPM in today’s world. These aren’t lofty certifications - just practical things that make your day-to-day easier.</p>
<h2 id="understand-the-system">Understand the system</h2>
<p>You don&#39;t need to design the architecture. But when someone pulls up a diagram, you should be able to follow along.</p>
<p>Start with the basics - APIs, queues, microservices, events, caching. Not so you can lecture anyone, but so the conversation makes sense. Then go one step further: spin up a local dev environment. As I mentioned in the previous post, doing this even once gives you a whole new appreciation for what engineers deal with daily. Dependency hell, environment variables, build steps - the works.</p>
<p>From there, learn enough cloud to be dangerous. Containers, serverless, CI/CD, what &quot;region&quot; and &quot;quota&quot; and &quot;scaling&quot; actually mean in practice. You don&#39;t need to run Kubernetes clusters. You just need the vocabulary to keep up - and the instinct to know when something sounds harder than it should be.</p>
<h2 id="follow-the-work">Follow the work</h2>
<p>This is where a lot of TPMs fall behind - and where the gap between good and great really opens up.</p>
<p>Learn how engineering actually works. Code reviews, branching strategies, feature flags, testing pipelines, linters, release cycles. Not so you can manage them - so you can plan around them. So when an engineer says &quot;<em>that&#39;ll need a flag</em>&quot; or &quot;<em>we are code complete</em>&quot;, you know exactly what they mean and why it matters.</p>
<p>Get comfortable reading logs and dashboards too. You don&#39;t need to be on-call. But poking around metrics when something goes wrong - understanding the shape of a problem before the postmortem - makes you genuinely useful in those moments rather than just present.</p>
<p>And read the docs. Design docs, API Docs, and postmortems. You don&#39;t need to absorb every detail. You just need to skim well enough to spot a risk before it becomes a crisis. It&#39;s a small habit that compounds fast.</p>
<h2 id="speak-the-language">Speak the language</h2>
<p>Here&#39;s the one that often gets overlooked: learn to read code.</p>
<p>Not write it - read it. Follow a pull request. Understand the logic. Tinker with a small script now and then. In the previous post I mentioned keeping a GitHub account and doing a bit of vibe coding just to stay sharp - this is exactly why. It keeps you connected to the craft in a way that&#39;s hard to replicate any other way. And it builds real empathy for engineers doing this every day, under pressure, at pace.</p>
<p>Pick up some basic data fluency while you&#39;re at it. Running a small query, calling an API, building a quick graph - these are small things that let you answer questions yourself instead of waiting on someone else every time.</p>
<p>And take AI seriously. Not as a buzzword - as a genuine shift in how engineering teams work. Understanding how your teams actually use it, how it affects velocity, how it changes estimation and debugging - that&#39;s table stakes now. Personally, I&#39;ve found it invaluable when getting up to speed on a new codebase.</p>
<h2 id="the-2026-layer-ai-is-now-part-of-the-system-youre-managing">The 2026 layer: AI is now part of the system you&#39;re managing</h2>
<p>This is where I&#39;d add something that wasn&#39;t in my <a href="https://deanhume.com/staying-technical-as-a-technical-program-manager/">original post</a> - because it&#39;s genuinely new territory, and most TPMs haven&#39;t caught up yet.</p>
<p>AI is no longer just a productivity tool sitting alongside engineering. It&#39;s becoming part of the system itself. That changes what you need to know.</p>
<h3 id="understand-ai-agent-orchestration">Understand AI agent orchestration</h3>
<p>Teams aren&#39;t just using AI to write code anymore. They&#39;re running autonomous agents to handle whole chunks of work - prototyping, testing, triaging, even making decisions. Rather than one massive agent that attempts everything, orchestration coordinates multiple specialised agents, with each one handling what it does best.</p>
<figure><img src="https://deanhume.com/content/posts/technical-skills-every-tpm-should-learn-in-2026/open-ai-technical-program-manager.jpg" alt="Understanding AI as a TPM is a key skill" loading="lazy"></figure>
<p>As a TPM, you need to understand how these pipelines are structured, where they can fail, and how to think about estimating work when part of the team is an agent. <a href="https://www.gartner.com/en/newsroom/press-releases/2025-08-26-gartner-predicts-40-percent-of-enterprise-apps-will-feature-task-specific-ai-agents-by-2026-up-from-less-than-5-percent-in-2025">Gartner forecasts</a> that 40% of enterprises will have embedded AI agents by the end of 2026 - this is not a future problem. It&#39;s a now problem.</p>
<h3 id="know-what-mcp-is">Know what MCP is</h3>
<p><a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol</a> has quietly become one of the most important standards in AI engineering right now. It&#39;s an open-source standard for connecting AI applications to external systems - data sources, tools, workflows - enabling them to access key information and perform tasks. Think of it like a USB-C port for AI. You don&#39;t need to build one. But knowing what it is, why teams reach for it, and how it fits into a broader agent architecture puts you ahead of most TPMs right now. <a href="https://cloud.google.com/discover/what-is-model-context-protocol">Google&#39;s explainer</a> is a good place to start.</p>
<h3 id="rethink-how-you-estimate">Rethink how you estimate</h3>
<p>AI-assisted teams don&#39;t move at the same pace as traditional engineering teams - and the risk profile is different too. Features that used to take two weeks might take two days. But new failure modes appear: hallucinations, evals regressing, model updates breaking behaviour in subtle ways. Your planning instincts need updating. What does a sprint look like when part of the work is being done by an agent? What does &quot;done&quot; mean when the output is probabilistic? These are questions worth sitting with.</p>
<h3 id="get-a-basic-handle-on-ai-governance">Get a basic handle on AI governance</h3>
<p>This one is landing on TPM plates fast - especially if you&#39;re in a regulated industry or shipping to enterprise customers. In the EU, the AI Act is already phasing in requirements, with broader applicability arriving in 2026. In the US, state-level laws are also emerging.</p>
<figure><img src="https://deanhume.com/content/posts/technical-skills-every-tpm-should-learn-in-2026/ai-governanace-technical-program-manager.jpg" alt="Understanding AI governance as a Technical Program Manager" loading="lazy"></figure>
<p>You don&#39;t need to be a compliance expert, but understanding concepts like model versioning, audit trails, bias testing, and what &quot;high-risk AI&quot; means in a regulatory context helps you ask the right questions before they become blockers. <a href="https://www.onetrust.com/blog/responsible-ai-in-2026-a-3-step-guide-for-governance-that-scales/">This overview from OneTrust</a> is a useful starting point.</p>
<h2 id="one-last-thought">One last thought</h2>
<p>What&#39;s helped me as a TPM is keeping the habits small. Read the PR. Skim the doc. Run the build. Poke the logs. None of it takes long, but it compounds in a way that&#39;s hard to explain until you feel it - that moment where you&#39;re in a conversation and you actually know what&#39;s going on, rather than pattern-matching your way through it.</p>
<p>The AI layer makes this a bit more pressing right now, because the ground is shifting quickly. But the underlying idea is the same one from my <a href="https://deanhume.com/staying-technical-as-a-technical-program-manager/">previous post</a> - staying technical isn&#39;t about being the smartest person in the room. It&#39;s just about staying curious enough to be useful.👍</p>]]></content:encoded></item><item><title><![CDATA[Supercharge Your Clipboard with PowerToys Advanced Paste 📋]]></title><description><![CDATA[PowerToys Advanced Paste transforms your clipboard - paste as plain text, Markdown, JSON, extract text from images, and reformat content with AI.]]></description><link>https://deanhume.com/powertoys-advanced-paste-clipboard-supercharged/</link><guid isPermaLink="false">https://deanhume.com/powertoys-advanced-paste-clipboard-supercharged/</guid><category><![CDATA[Technical Program Manager]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Tue, 28 Apr 2026 16:15:22 GMT</pubDate><media:content url="https://deanhume.com/content/posts/powertoys-advanced-paste-clipboard-supercharged/powertoys-advanced-ai-paste-2-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/powertoys-advanced-paste-clipboard-supercharged/powertoys-advanced-ai-paste-2-1280.webp" alt="Supercharge Your Clipboard with PowerToys Advanced Paste 📋"><p>A few weeks ago, I wrote about how <a href="https://deanhume.com/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/">PowerToys Workspaces</a> helped me stop wasting time arranging windows every morning. Since then, a few people asked what else I use from the PowerToys suite. So today I want to talk about another tool that&#39;s quietly become part of my daily workflow - <strong>Advanced Paste</strong>.</p>
<p>Have you ever copied something from a webpage, pasted it into a doc, and then spent the next two minutes stripping out all the weird formatting? Bold text that shouldn&#39;t be bold, font sizes that don&#39;t match, colours that make no sense. Yeah. It&#39;s one of those small annoyances that adds up over time.</p>
<p>Advanced Paste fixes that - and a whole lot more.</p>
<h2 id="what-is-advanced-paste">What is Advanced Paste?</h2>
<p>Advanced Paste is a clipboard management tool built into PowerToys. The idea is simple: instead of just pasting whatever is on your clipboard as-is, you get to choose <em>how</em> it gets pasted. Plain text, Markdown, JSON, even as a file - all from a single keyboard shortcut.</p>
<p>Think of it like having a smart clipboard that knows what you actually need, rather than blindly dumping whatever formatting came along for the ride.</p>
<p>You open it with <code>Win + Shift + V</code> by default, and a clean little window appears with your paste options. That&#39;s it.</p>
<figure><img src="https://deanhume.com/content/posts/powertoys-advanced-paste-clipboard-supercharged/image-2.png" alt="PowerToys Advanced Paste - AI" loading="lazy"></figure>
<p>PowerToys Advanced Paste - AI</p>
<h2 id="paste-as-plain-text">Paste as Plain Text</h2>
<p>This is the one I use most. Copy something from a browser or a rich text editor, hit the shortcut, and paste it as pure unformatted text. No bold. No headings. No leftover HTML weirdness.</p>
<p>It sounds almost too simple - but honestly, that&#39;s the point. Removing that friction from something you do dozens of times a day is worth a lot more than it sounds.</p>
<p>You can also set a direct keyboard shortcut for this so you don&#39;t even need to open the Advanced Paste window. Just copy, hit your shortcut, and you&#39;re done.</p>
<h2 id="paste-as-markdown">Paste as Markdown</h2>
<p>Got some HTML on your clipboard and need it in Markdown? Advanced Paste handles that conversion for you automatically. So something like:</p>
<pre><code>&lt;b&gt;Paste&lt;/b&gt; &lt;i&gt;as&lt;/i&gt; &lt;a href=&quot;...&quot;&gt;Markdown&lt;/a&gt;</code></pre>
<p>Becomes:</p>
<pre><code>**Paste** *as* [Markdown](...)</code></pre>
<p>If you write documentation, blog posts, or work in any tool that uses Markdown - this one will save you a surprising amount of time.</p>
<h2 id="paste-as-json">Paste as JSON</h2>
<p>Similar idea, but for structured data. If you&#39;ve got XML or some other structured text on your clipboard and need it in JSON format, Advanced Paste converts it on the fly. No manual reformatting, no copy-pasting into a converter website.</p>
<p>As someone who spends time working across different tools and formats, this one is genuinely useful.</p>
<h2 id="paste-as-a-file">Paste as a File</h2>
<p>This is where it gets a bit more interesting. Advanced Paste can take what&#39;s on your clipboard and paste it directly as a file - a <code>.txt</code> file, an <code>.html</code> file, or even a <code>.png</code> file for images.</p>
<p>The <code>.html</code> option is particularly handy if you&#39;re copying a section of a webpage (links, formatted text, images and all) and want to save it as a proper file rather than lose all that structure.</p>
<h2 id="image-to-text-ocr">Image to Text (OCR)</h2>
<p>Have you ever needed to copy text from a screenshot or an image? Normally that means manually typing it out, which is tedious at best.</p>
<p>Advanced Paste has a built-in OCR feature that pulls the text straight out of an image on your clipboard. It all runs locally on your machine - no data being sent anywhere. Just copy the image, open Advanced Paste, hit &quot;Image to Text&quot;, and the extracted text is ready to paste.</p>
<p>I&#39;ve used this more times than I expected - screenshots from Teams calls, error messages in images, text in PDFs that won&#39;t let you select it. It just works.</p>
<h2 id="paste-with-ai">Paste with AI 🤖</h2>
<p>This is the most powerful feature in the set, and it&#39;s worth calling out separately.</p>
<p>If you configure an AI model provider - OpenAI, Azure OpenAI, Mistral, Google, or a local model via Ollama or Foundry Local - you unlock a whole new level of clipboard transformation. You can type a prompt and the AI will reformat or rewrite your clipboard content based on your instructions.</p>
<p>Some examples of what you can do:</p>
<ul>
<li>Summarise a long block of text</li>
<li>Translate text into another language</li>
<li>Rewrite something in a more professional tone</li>
<li>Generate code from a description</li>
<li>Reformat data into a specific structure</li>
</ul>
<p>The local model options (Ollama, Foundry Local) are worth a look if you&#39;d rather keep everything on your machine and not worry about API costs or data leaving your device.</p>
<p>You can also save your most-used prompts as <strong>custom actions</strong> and assign them keyboard shortcuts. So if you regularly need to &quot;translate to French and clean up formatting&quot;, you can do that in one keypress without even opening the window.</p>
<h2 id="setting-it-up">Setting It Up</h2>
<p>If you haven&#39;t already got PowerToys installed, grab it from the <a href="https://aka.ms/getPowertoys">Microsoft Store</a> or via WinGet. Then:</p>
<figure><img src="https://deanhume.com/content/posts/powertoys-advanced-paste-clipboard-supercharged/image.png" alt="Setting up Powertoys Advanced Paste" loading="lazy"></figure>
<p>Setting up Powertoys Advanced Paste</p>
<ol>
<li>Open <strong>PowerToys Settings</strong> and navigate to <strong>Advanced Paste</strong>.</li>
<li>Enable <strong>Paste with AI.</strong></li>
<li>I added Google&#39;s Gemini as a Model Provider. You&#39;ll need to enter an API key, but that is easily done depending on your AI Model Provider.</li>
</ol>
<figure><img src="https://deanhume.com/content/posts/powertoys-advanced-paste-clipboard-supercharged/image-1.png" alt="Add an AI model provider" loading="lazy"></figure>
<p>Add an AI model provider</p>
<p>Once you&#39;ve completed all those steps, it should be working as expected. The default shortcut (<code>Win + Shift + V</code>) is easy to remember - think of it as an upgraded version of the regular paste you already know.</p>
<h2 id="a-small-tool-with-a-big-impact">A Small Tool with a Big Impact</h2>
<p>I&#39;ll be honest - I didn&#39;t expect Advanced Paste to become something I reach for every day. But like Workspaces, it&#39;s one of those tools that solves a real friction point so well that you quickly forget what life was like without it.</p>
<p>If you&#39;re already using PowerToys, enable it today. If you&#39;re not using PowerToys yet - what are you waiting for? 😄</p>
<p>The <a href="https://learn.microsoft.com/en-us/windows/powertoys/advanced-paste">Advanced Paste docs</a> are a great place to explore everything it can do.</p>]]></content:encoded></item><item><title><![CDATA[How I Use PowerToys Workspaces to Switch Contexts in Two Clicks 🫰]]></title><description><![CDATA[Tired of arranging windows every morning? PowerToys Workspaces launches your ideal Windows desktop management setup with two clicks 🫰.]]></description><link>https://deanhume.com/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/</link><guid isPermaLink="false">https://deanhume.com/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/</guid><category><![CDATA[Technical Program Manager]]></category><category><![CDATA[Writing]]></category><category><![CDATA[Powertoys]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Thu, 12 Mar 2026 14:32:13 GMT</pubDate><media:content url="https://deanhume.com/content/posts/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/powertoys-workspaces-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/powertoys-workspaces-1280.webp" alt="How I Use PowerToys Workspaces to Switch Contexts in Two Clicks 🫰"><p>Have you ever sat down at your computer, ready to get into a flow state, and then spent the first ten minutes just <em>arranging windows</em>? Dragging your editor here, your browser there, nudging Slack into a corner - only to close it all two hours later, and start from scratch the next day? Yeah. Me too.</p>
<p>I&#39;ve been a big fan of <a href="https://learn.microsoft.com/en-us/windows/powertoys/">Microsoft PowerToys</a> for a while now. It&#39;s one of those tools that quietly makes your Windows experience feel a lot more intentional. There are a bunch of great utilities in there - but recently I&#39;ve been leaning heavily on one in particular: <strong>PowerToys Workspaces</strong>.</p>
<h2 id="what-is-powertoys-workspaces">What is PowerToys Workspaces?</h2>
<p>Workspaces is a Windows desktop management utility that lets you capture your ideal window layout, which apps are open, where they&#39;re positioned, what size they are, and then launch that entire setup with a single click.</p>
<p>Think of it like a saved game state, but for your desktop.</p>
<p>You could have a &quot;Coding&quot; workspace that opens VS Code to a specific project, a browser on the side, and Terminal ready to go. Or an &quot;Email &amp; Admin&quot; workspace that brings up Outlook, your calendar, and a notes app. Whatever your workflow looks like - you set it up once, and Workspaces handles the rest every time.</p>
<h2 id="setting-up-your-first-workspace">Setting Up Your First Workspace</h2>
<p>If you haven&#39;t already got <a href="https://learn.microsoft.com/en-us/windows/powertoys/">PowerToys</a> installed, you can grab it from the <a href="https://aka.ms/getPowertoys">Microsoft Store</a> or via <a href="https://winget.run/pkg/Microsoft/PowerToys">WinGet</a>. Once it&#39;s running, head into the PowerToys Settings and enable <strong>Workspaces</strong>.</p>
<p>To create your first workspace:</p>
<ol>
<li>Hit <code>Win + Ctrl + &#39;</code> to open the Workspaces editor (or go to Settings and click &quot;Launch editor&quot;).</li>
<li>Click <strong>&quot;+ Create workspace&quot;.</strong></li>
<li>You&#39;ll enter Capture mode - arrange your apps exactly how you want them.</li>
<li>When you&#39;re happy hit <strong>&quot;Capture&quot;.</strong></li>
<li>Give it a name, tweak anything you need to, and save it.</li>
</ol>
<figure><img src="https://deanhume.com/content/posts/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/image.png" alt="PowerToys Workspaces Editor showing a saved daily workspace" loading="lazy"></figure>
<p>That&#39;s it. When it you launch it, it will open those apps exactly as you&#39;d like them. You can create as many different workspaces as you&#39;d like depending on your needs.</p>
<h2 id="the-part-that-surprised-me">The Part That Surprised Me</h2>
<p>What I didn&#39;t expect was how useful the <strong>CLI arguments</strong> feature would be. When you&#39;re in the editor, each app has a little dropdown where you can pass command line arguments. So for VS Code, I can point it straight to a specific project folder on launch. For Edge, I can give it a comma-separated list of URLs and it&#39;ll open all those tabs automatically.</p>
<p>It sounds like a small thing - but removing those few extra clicks every time you start a session adds up surprisingly quickly.</p>
<h2 id="desktop-shortcut">Desktop Shortcut</h2>
<p>Within the workspace editor, you can select the option to <strong>Create a desktop shortcut</strong> which creates a handy shortcut on your desktop.</p>
<figure><img src="https://deanhume.com/content/posts/how-i-use-powertoys-workspaces-to-switch-contexts-in-two-clicks/image-1.png" alt="A shortcut on my desktop made by PowerToys Workspaces" loading="lazy"></figure>
<p>A shortcut on my desktop made by PowerToys Workspaces</p>
<p>I find this super handy for when I start my PC first thing in the morning. Just a double click and your workspace is loaded and ready to go. If you prefer, you could pin it to your taskbar if you really want it front and centre.</p>
<h2 id="a-few-things-worth-knowing">A Few Things Worth Knowing</h2>
<p>When a workspace launches, you&#39;ll briefly see windows jumping around the screen as PowerToys repositions them. There&#39;s a status dialog that pops up to show you what&#39;s loading, which helps - but it can look a bit chaotic the first time you see it.</p>
<p>Also worth noting: if you&#39;ve got apps snapped using Windows&#39; built-in snap feature, Workspaces won&#39;t preserve that snapped state. It uses its own positioning engine under the hood. Not a dealbreaker, but something to be aware of when you&#39;re setting things up.</p>
<h2 id="how-i-use-it-day-to-day">How I Use It Day to Day</h2>
<p>If you&#39;ve read my recent post on <a href="https://deanhume.com/staying-technical-as-a-technical-program-manager/">staying technical as a TPM</a>, you&#39;ll know I&#39;m always looking for ways to reduce friction and stay in a coding mindset. Workspaces has become one of those small but surprisingly impactful habits.</p>
<p>I&#39;ve ended up with two workspaces that cover probably 90% of my week:</p>
<ul>
<li><strong>Coding</strong> - VS Code, Terminal, and a browser window with the relevant docs.</li>
<li><strong>Daily</strong> - Outlook, Teams, Slack and a browser.</li>
</ul>
<p>Switching between them takes a double click. It sounds almost too simple - but honestly, that&#39;s the point. The friction of setting up your environment is just gone.</p>
<ul>
<li><em> </em></li>
</ul>
<p>If you haven&#39;t explored PowerToys yet, it&#39;s one of the best free productivity tools for Windows and the <a href="https://learn.microsoft.com/en-us/windows/powertoys/workspaces">Workspaces docs</a> are a great place to start.</p>]]></content:encoded></item><item><title><![CDATA[Staying Technical as a Technical Program Manager]]></title><description><![CDATA[Learn practical ways Technical Program Managers can stay technical - daily habits, internal engineering practices, and hands-on routines that keep skills sharp while leading programs at scale.]]></description><link>https://deanhume.com/staying-technical-as-a-technical-program-manager/</link><guid isPermaLink="false">https://deanhume.com/staying-technical-as-a-technical-program-manager/</guid><category><![CDATA[Game Development]]></category><category><![CDATA[Technical Leadership]]></category><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Wed, 18 Feb 2026 11:39:39 GMT</pubDate><media:content url="https://deanhume.com/content/posts/staying-technical-as-a-technical-program-manager/coding-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/staying-technical-as-a-technical-program-manager/coding-1280.webp" alt="Staying Technical as a Technical Program Manager"><p>In my career, I&#39;ve been many things - a Support Engineer, a Software Engineer and an Engineering Manager. These days, I&#39;ve settled into a Technical Program Manager role (which I am thoroughly enjoying).</p>
<p>As a Technical Program Manager (or TPM), you&#39;re often expected to wear multiple hats - strategic planner, cross-functional communicator, and technical problem solver. But how do you stay technically sharp while managing programs at scale?</p>
<p>The role of a TPM is different from a program manager, you <em>are</em> expected to be <strong>technical</strong>. Here are some of the daily (and weekly) practices that help me keep my technical skills sharp.</p>
<h3 id="writing-the-occasional-code">Writing the occasional code</h3>
<p>Early in my career, I was a software engineer. These days, my role doesn’t require me to write code, but I still have the urge to explore and tinker. I’ve built a bunch of small tools and love updating my <a href="https://github.com/deanhume/">GitHub account</a>. I’m also big into AI right now (as is everyone 😂) and have been doing a lot of “vibe coding” just to learn something new or scratch an itch. Even writing a few lines here and there keeps your brain tuned to real engineering challenges.</p>
<h3 id="writing-technical-content">Writing Technical Content</h3>
<p>Blogging has been a powerful tool for learning. Whether it’s writing about <a href="https://deanhume.com/azure-hybrid-and-embedded-text-to-speech/">Azure Text-to-Speech</a>, troubleshooting <a href="https://deanhume.com/tag/ghost-tag/">Ghost CMS</a>, or <a href="https://deanhume.com/tag/game-development/">Game Development</a>, the act of explaining technical concepts forces clarity and deepens my understanding. It also helps others, which is always a bonus.</p>
<h3 id="following-tech-blogs">Following tech blogs</h3>
<p>One of the most useful habits I’ve developed is staying up to date with what’s happening across the tech world. Even if it’s not tech I use every day, it’s important to see how others are solving problems. You never know when a piece of tech or a pattern will end up being useful later.</p>
<h3 id="insert-your-hobby-here">&lt;Insert your hobby here&gt;</h3>
<p>Outside of work, I love brewing my own beer. I even have a <a href="https://humebrew.com/">blog</a> dedicated to it! One of the best ways to learn something new is to combine your hobby with tech. Recently, I built a <a href="https://deanhume.com/using-a-raspberry-pi-to-track-the-progress-of-your-homebrew/">fermentation tracker using a Raspberry Pi</a> and temperature sensors. It keeps me close to hardware and scripting, and it’s a fun, low-stakes way to apply engineering principles.</p>
<p>Balancing technical depth with program management isn’t about doing everything - it’s about staying curious, being useful, and knowing when to dive in. Whether it’s through side projects, internal tools, or collaborative problem‑solving, staying hands‑on helps me lead with more confidence and empathy. It might all sound obvious, but the real impact has come from deliberately doing these things, day after day, with intention.</p>
<h2 id="how-to-stay-close-to-your-companys-tech">How to Stay Close to Your Company&#39;s Tech</h2>
<p>While it’s important to understand what’s happening across the wider tech landscape, it’s just as important (if not more so) to stay close to the technology <em>inside</em> your own organisation. Knowing how your systems work, how your teams build, and where the real challenges live helps you make better decisions and builds trust with engineering.</p>
<p>Here are some of the things I try to do in my role to stay close to the tech:</p>
<p><strong>Read Your Company’s Engineering Docs</strong> Most teams have design docs, architecture overviews, or RFCs floating around. Even skimming them gives you a deeper understanding of how things fit together and why certain decisions were made.</p>
<p><strong>Sit In on Architecture or Design Reviews</strong> You don’t necessarily need to weigh in on every detail, but observing the discussions helps you understand trade‑offs, constraints, and how engineers think about the system.</p>
<p><strong>Spin Up a Local Dev Environment</strong> Even if you’re not coding every day, setting up and running the services your teams build helps you understand the tooling, dependencies, and workflows they use.</p>
<p><strong>Use the Product Like a Real User</strong> If your company makes consumer or developer-facing tools, actually use them end-to-end. It sharpens your instincts for what “good” feels like and what’s actually painful.</p>
<h3 id="why-staying-technical-matters-for-tpms"><strong>Why Staying Technical Matters for TPMs</strong></h3>
<ul>
<li><strong>Credibility with Engineering Teams</strong>: Understanding the tech stack helps you ask the right questions and gain trust. This is especially important in a gaming role! If engineers don&#39;t believe you are credible and have the background knowledge, it becomes much harder to lead effectively.</li>
<li><strong>Better Decision-Making</strong>: Technical fluency helps you assess trade-offs, spot risks, and understand what “good” looks like.</li>
<li><strong>Faster Problem Resolution</strong>: When you understand the underlying systems, you can catch bottlenecks or misalignments earlier.</li>
</ul>
<h3 id="time-management">Time Management</h3>
<p>So how do I fit all this learning time in?</p>
<p>In a busy work environment, it can be tricky to stay current while balancing program responsibilities. I also have a family, and being a present dad is non-negotiable.</p>
<p>One thing that has helped tremendously is <em>deliberately</em> blocking time for learning or tinkering - without compromising program delivery. For example, I use <a href="https://feedly.com/">Feedly</a> to read all the RSS feeds I subscribe to. First thing in the morning with a cup of coffee, I catch up on what&#39;s happening in our industry.</p>
<p>At Microsoft, we also have <a href="https://techcommunity.microsoft.com/blog/mvp-blog/microsoft-global-hackathon-2025-mvps-driving-innovation-across-communities/4442513">company-wide hackathons</a>, and they’re a fantastic opportunity to try something new, collaborate with people you’ve never worked with, and solve real technical challenges. I try to participate whenever I can.</p>
<p>Balancing learning and work isn’t easy, but scheduling time for both makes it achievable.</p>
<h3 id="conclusion">Conclusion</h3>
<p>I hope you’ve found this post useful. These are the things that have personally helped me stay technically sharp - they might not all be right for you, but it’s a good place to start. For me, it ultimately comes down to staying curious, being deliberate, and consistently carving out time to keep learning.</p>
<p>If you&#39;re a Technical Program Manager, how do you stay technical? I’d love to hear your approach.</p>]]></content:encoded></item><item><title><![CDATA[Backup Buddy: A Simple Tool to Backup Your Website Content]]></title><description><![CDATA[Backup Buddy turns your website sitemap into a portable Markdown archive with local images and videos, ready for offline reading or a platform move.]]></description><link>https://deanhume.com/backup-buddy-a-simple-tool-to-backup-your-website-content/</link><guid isPermaLink="false">https://deanhume.com/backup-buddy-a-simple-tool-to-backup-your-website-content/</guid><dc:creator><![CDATA[Dean Hume]]></dc:creator><pubDate>Mon, 01 Dec 2025 12:33:06 GMT</pubDate><media:content url="https://deanhume.com/content/posts/backup-buddy-a-simple-tool-to-backup-your-website-content/logo-1280.webp" medium="image"/><content:encoded><![CDATA[<img src="https://deanhume.com/content/posts/backup-buddy-a-simple-tool-to-backup-your-website-content/logo-1280.webp" alt="Backup Buddy: A Simple Tool to Backup Your Website Content"><p>Have you ever wanted to create a backup of your website or blog? Maybe you&#39;re migrating to a new platform, or perhaps you just want a portable archive of your content that you can read offline. I&#39;ve previously <a href="https://deanhume.com/ghost-blog-fixing-no-space-left-on-device-issue">experienced</a> <a href="https://deanhume.com/amazon-aws-ec2-ghost-cms-setup/">issues</a> with this site going down and I wanted to make sure that I wasn&#39;t stuck without a backup of all my posts.</p>
<p>That&#39;s exactly why I built <strong>Backup Buddy</strong> - a command-line tool that turns any website into a collection of clean, readable Markdown files with all the images (and videos) preserved.</p>
<h2 id="what-does-backup-buddy-do">What Does Backup Buddy Do?</h2>
<p>Backup Buddy takes a website&#39;s sitemap and automatically downloads every page, converting the HTML into clean Markdown format while saving all the images (and videos) locally. Think of it as creating a time capsule of your website that you can read, search, and store anywhere - no internet required.</p>
<p>The best part? You end up with content in Markdown format, which means you can:</p>
<ul>
<li>Read it in any text editor</li>
<li>Import it into a static site generator like Hugo or Jekyll</li>
<li>Search through it with standard text tools</li>
<li>Version control it with Git</li>
<li>Share it without worrying about broken links or missing images</li>
</ul>
<h2 id="how-to-use-it">How to Use It</h2>
<p>Using Backup Buddy is straightforward. You just need to know your website&#39;s sitemap URL (usually something like <code>https://yoursite.com/sitemap.xml</code>) and run a single command:</p>
<pre><code class="language-bash">backup-buddy https://yoursite.com/sitemap.xml</code></pre>
<p>That&#39;s it! The tool will:</p>
<ol>
<li>Download your sitemap</li>
<li>Find all the URLs</li>
<li>Download each page</li>
<li>Convert the HTML to Markdown</li>
<li>Save all the images</li>
<li>Organize everything into neat folders</li>
</ol>
<h3 id="getting-started">Getting Started</h3>
<p>Head over to the Git repo and <a href="https://github.com/deanhume/backup-buddy/releases/tag/v1">download the latest release</a>.</p>
<h2 id="what-you-get">What You Get</h2>
<p>After running Backup Buddy, you&#39;ll find an <code>output</code> folder with all your content organized like this:</p>
<pre><code>output/
├── 1_my-first-post/
│   ├── my-first-post.md
│   ├── metadata.txt
│   ├── images/
│   │   ├── header-image.jpg
│   │   └── diagram.png
│   └── videos/
│       └── demo.mp4
├── 2_another-post/
│   ├── another-post.md
│   ├── metadata.txt
│   └── images/
│       └── photo.jpg
└── ...</code></pre>
<p>Each page gets its own folder containing:</p>
<ul>
<li>The Markdown version of your content</li>
<li>A metadata file with the original URL and backup date</li>
<li>An images folder with all pictures from that page</li>
<li>A videos folder with any videos from that page</li>
</ul>
<h2 id="why-i-built-this">Why I Built This</h2>
<p>I&#39;ve been blogging for years, and I&#39;ve migrated platforms more than once. Each time, I worried about losing content, breaking image links, or dealing with complicated export tools. I wanted something simple that would give me a clean backup I could actually use.</p>
<p>The Markdown format is perfect because it&#39;s:</p>
<ul>
<li><strong>Human-readable</strong> - You can open it in any text editor</li>
<li><strong>Future-proof</strong> - It&#39;s just text, so it&#39;ll work forever</li>
<li><strong>Portable</strong> - Easy to import into almost any blogging platform or static site generator</li>
<li><strong>Git-friendly</strong> - You can track changes and collaborate on content</li>
</ul>
<h2 id="performance-features">Performance Features</h2>
<p>One thing I made sure to include was speed. Backup Buddy processes multiple pages at once (10 by default), so even large websites with hundreds of pages get backed up quickly. You&#39;ll see real-time progress as it works:</p>
<pre><code>[1/150] Processing: https://example.com/first-post
[2/150] Processing: https://example.com/second-post
  ✓ Saved to: output/1_first-post
  ✓ Saved to: output/2_second-post
[3/150] Processing: https://example.com/third-post
...</code></pre>
<h2 id="a-few-things-to-know">A Few Things to Know</h2>
<p>While Backup Buddy handles most websites well, there are a few limitations:</p>
<ul>
<li>It only backs up URLs in your sitemap (which is usually everything important)</li>
<li>Content that&#39;s loaded with JavaScript after the page loads won&#39;t be captured</li>
<li>Images need to be accessible when you run the backup</li>
<li>Very large sites might need you to adjust the parallel processing settings</li>
</ul>
<h2 id="try-it-out">Try It Out</h2>
<p>If you&#39;ve been looking for a simple way to backup your website or blog, give Backup Buddy a try. It&#39;s open source (MIT License), so you can use it freely and even modify it for your needs.</p>
<p>Check it out on GitHub: <a href="https://github.com/deanhume/backup-buddy">github.com/deanhume/backup-buddy</a></p>
<p>Have questions or suggestions? Feel free to open an issue or submit a pull request. Happy archiving!</p>]]></content:encoded></item></channel></rss>