<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Daniel Schroeder</title>
    <description>The latest articles on Forem by Daniel Schroeder (@deadlydog).</description>
    <link>https://forem.com/deadlydog</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F128141%2F6f6ad200-8993-46ca-8c4a-f44ea0a236b6.jpeg</url>
      <title>Forem: Daniel Schroeder</title>
      <link>https://forem.com/deadlydog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/deadlydog"/>
    <language>en</language>
    <item>
      <title>Host website PR previews on GitHub Pages for free</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/host-website-pr-previews-on-github-pages-for-free-5215</link>
      <guid>https://forem.com/deadlydog/host-website-pr-previews-on-github-pages-for-free-5215</guid>
      <description>&lt;p&gt;GitHub Pages allows you to host one static website per repository for free.
The URL is typically in the format &lt;code class="language-plaintext highlighter-rouge"&gt;https://&amp;lt;username&amp;gt;.github.io/&amp;lt;repository&amp;gt;/&lt;/code&gt;, and you can use it to host your project’s documentation, portfolio, or any static content.
I use it to host this blog for free, along with a custom domain name.&lt;/p&gt;

&lt;h2 id="the-problem-hosting-pr-previews"&gt;The problem; hosting PR previews&lt;/h2&gt;

&lt;p&gt;Recently, I wanted to preview some changes I was making to my blog before merging them into the main branch.
I can run the site locally on my PC, but wanted to preview it from my phone, and share the link with my wife to get her feedback.&lt;/p&gt;

&lt;p&gt;Since GitHub Pages only gives you one URL per repo, the typical advice is to use a 3rd party hosting service like Netlify, Vercel, AWS, or Azure to host PR preview sites.
You can use a GitHub Actions workflow to deploy the preview version of your site to the external service when a PR is opened, and it will get a unique URL that you can share with others.
GitHub even has official documentation on how to &lt;a href="https://docs.github.com/en/actions/how-tos/deploy/deploy-to-third-party-platforms/azure-static-web-app" rel="noopener noreferrer"&gt;deploy PRs to services like Azure Static Web Apps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Even though some of these services have free tiers, I didn’t want to sign up for another hosting platform, deal with API keys, or depend on a 3rd party service just for PR previews.
I wanted everything to remain in GitHub.&lt;/p&gt;

&lt;p&gt;There are long-standing issues asking GitHub to allow hosting PR previews directly on GitHub Pages, such as &lt;a href="https://github.com/orgs/community/discussions/7730" rel="noopener noreferrer"&gt;this one&lt;/a&gt;.
Unfortunately, it doesn’t look like it will be implemented anytime soon.
Luckily, I found a workaround.&lt;/p&gt;

&lt;h2 id="a-simple-solution"&gt;A simple solution&lt;/h2&gt;

&lt;p&gt;Rather than hosting multiple static websites on different subdomains, simply host them all on the same domain, but in different subdirectories.
Essentially, your PR previews can be hosted inside your production website, just in a deeper path.&lt;/p&gt;

&lt;p&gt;So to visit your production site you would go to &lt;code class="language-plaintext highlighter-rouge"&gt;https://&amp;lt;username&amp;gt;.github.io/&amp;lt;repository&amp;gt;/&lt;/code&gt;, and to visit your PR preview site you might go to &lt;code class="language-plaintext highlighter-rouge"&gt;https://&amp;lt;username&amp;gt;.github.io/&amp;lt;repository&amp;gt;/previews/pr-123/&lt;/code&gt;, where &lt;code class="language-plaintext highlighter-rouge"&gt;123&lt;/code&gt; is the PR number.&lt;/p&gt;

&lt;h3 id="caveats"&gt;Caveats&lt;/h3&gt;

&lt;p&gt;There are some caveats with this approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For non-generated sites, assets must be referenced with relative paths so they work in both the production and preview sites.
    &lt;ul&gt;
      &lt;li&gt;I use Jekyll as my static site generator and simply set the &lt;code class="language-plaintext highlighter-rouge"&gt;baseurl&lt;/code&gt; in the config when building the site.
All asset references are relative to the base URL.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The PR preview site is publicly accessible by default, which may not be desirable, especially for closed-source projects where you don’t want to expose unfinished work.&lt;/li&gt;
  &lt;li&gt;To prevent web crawlers from indexing the PR previews, you should update your &lt;code class="language-plaintext highlighter-rouge"&gt;robots.txt&lt;/code&gt; file to disallow crawling of the previews subdirectory.&lt;/li&gt;
  &lt;li&gt;Your production site analytics may be skewed by traffic to the PR previews.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since my use case is simply my public open-source blog, these caveats are not a concern for me.&lt;/p&gt;

&lt;h2 id="an-example-workflow"&gt;An example workflow&lt;/h2&gt;

&lt;p&gt;The basic workflow is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;When a PR is opened, deploy the preview version of the site to a subdirectory named after the PR number, and post a comment in the PR with the URL to the preview site.&lt;/li&gt;
  &lt;li&gt;If the PR is updated, redeploy the preview site with the latest changes.&lt;/li&gt;
  &lt;li&gt;When the PR is closed, delete the preview site.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s an example PR comment with the preview URL that can get posted by the workflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.danskingdom.com%2Fassets%2FPosts%2F2026-04-07-Host-website-PR-previews-on-GitHub-Pages-for-free%2Fpr-comment-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.danskingdom.com%2Fassets%2FPosts%2F2026-04-07-Host-website-PR-previews-on-GitHub-Pages-for-free%2Fpr-comment-screenshot.png" alt="PR comment with preview URL screenshot" width="623" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s see how to configure the GitHub repo and set up GitHub Actions workflows to take care of this for us.
The examples shown are from my blog GitHub repo, for reference: &lt;a href="https://github.com/deadlydog/Blog" rel="noopener noreferrer"&gt;https://github.com/deadlydog/Blog&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="configuring-the-github-repo"&gt;Configuring the GitHub repo&lt;/h3&gt;

&lt;p&gt;You will need to configure your GitHub repo settings to enable GitHub Pages and to deploy from a branch, typically the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;In your GitHub repo settings:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Go to the &lt;code class="language-plaintext highlighter-rouge"&gt;Pages&lt;/code&gt; section.&lt;/li&gt;
  &lt;li&gt;Ensure the build and deployment Source is set to &lt;code class="language-plaintext highlighter-rouge"&gt;Deploy from a branch&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;For the Branch, select &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; and the root folder.&lt;/li&gt;
  &lt;li&gt;Optionally, if you have a custom domain name, you can set it up here as well.
    &lt;ul&gt;
      &lt;li&gt;This will require some DNS configuration to point your custom domain to GitHub Pages.
I will not cover it here, as you can read the GitHub documentation on how to set that up.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.danskingdom.com%2Fassets%2FPosts%2F2026-04-07-Host-website-PR-previews-on-GitHub-Pages-for-free%2Fgithub-pages-settings-screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.danskingdom.com%2Fassets%2FPosts%2F2026-04-07-Host-website-PR-previews-on-GitHub-Pages-for-free%2Fgithub-pages-settings-screenshot.png" alt="GitHub Pages settings screenshot" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now whenever changes are pushed to the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch, GitHub Pages will automatically build and deploy the site, and it will be available at the URL specified in the settings.&lt;/p&gt;

&lt;p&gt;This is how you host your production site.
The next step is to have PR previews automatically deployed to subdirectories within it.&lt;/p&gt;

&lt;h3 id="setting-up-github-actions-workflows"&gt;Setting up GitHub Actions workflows&lt;/h3&gt;

&lt;p&gt;You will need to set up 2 GitHub Actions workflows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;One to deploy the PR preview site when a PR is opened or updated, and to delete it when the PR is closed.&lt;/li&gt;
  &lt;li&gt;Another to deploy the production site to the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch when changes are merged into the main branch.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id="pr-preview-to-gh-pages-workflow"&gt;PR preview to gh-pages workflow&lt;/h4&gt;

&lt;p&gt;Below is an example workflow for deploying PR previews to a &lt;code class="language-plaintext highlighter-rouge"&gt;previews&lt;/code&gt; subdirectory within the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch.
It also updates the PR with a comment containing the URL to the preview site.
When the PR is closed, the workflow deletes the preview site from the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/deadlydog/Blog/blob/main/.github/workflows/pr-preview.yml" rel="noopener noreferrer"&gt;view the latest version of this pr-preview.yml workflow here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This workflow has some Jekyll-specific steps, but the general idea can be adapted to your needs.&lt;/p&gt;

&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Builds and deploys the Jekyll site for PRs to the "previews" directory on the&lt;/span&gt;
&lt;span class="c1"&gt;# gh-pages branch, and posts a comment with the preview URL to the PR.&lt;/span&gt;
&lt;span class="c1"&gt;# Also removes the preview from gh-pages when the PR is closed (merged or abandoned).&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy PR preview to gh-pages&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ─── Deploy preview on PR open / update ──────────────────────────────────&lt;/span&gt;
  &lt;span class="na"&gt;deploy-preview&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event.action != 'closed'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pr-preview-deploy-${{ github.event.pull_request.number }}&lt;/span&gt;
      &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby/setup-ruby@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.3'&lt;/span&gt;
          &lt;span class="na"&gt;bundler-cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Jekyll preview site&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pwsh&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JEKYLL_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
          &lt;span class="na"&gt;JEKYLL_GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Write a temporary config that overrides baseurl for the preview path.&lt;/span&gt;
          &lt;span class="s"&gt;@"&lt;/span&gt;
          &lt;span class="s"&gt;url: "https://blog.danskingdom.com"&lt;/span&gt;
          &lt;span class="s"&gt;baseurl: "/previews/pr-${{ github.event.pull_request.number }}"&lt;/span&gt;
          &lt;span class="s"&gt;"@ | Set-Content -Path /tmp/_config_preview.yml&lt;/span&gt;

          &lt;span class="s"&gt;bundle exec jekyll build --config _config.yml,/tmp/_config_preview.yml --destination ./_site_preview&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy preview to gh-pages branch previews subdirectory&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peaceiris/actions-gh-pages@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;publish_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./_site_preview&lt;/span&gt;
          &lt;span class="na"&gt;destination_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;previews/pr-${{ github.event.pull_request.number }}&lt;/span&gt;
          &lt;span class="na"&gt;keep_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;commit_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deploy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;preview&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PR&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;#${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.pull_request.number&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.sha&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}})"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post / Update preview URL comment on PR&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/github-script@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;const prNumber = context.payload.pull_request.number;&lt;/span&gt;
            &lt;span class="s"&gt;const previewUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/previews/pr-${prNumber}/`;&lt;/span&gt;
            &lt;span class="s"&gt;const marker = '&amp;lt;!-- pr-preview-bot --&amp;gt;';&lt;/span&gt;
            &lt;span class="s"&gt;const body =&lt;/span&gt;
              &lt;span class="s"&gt;`${marker}\n` +&lt;/span&gt;
              &lt;span class="s"&gt;`🔍 **PR Preview deployed:** ${previewUrl}\n\n` +&lt;/span&gt;
              &lt;span class="s"&gt;`_This preview is updated on every commit and will be removed when the PR is closed._`;&lt;/span&gt;

            &lt;span class="s"&gt;const { data: comments } = await github.rest.issues.listComments({&lt;/span&gt;
              &lt;span class="s"&gt;owner: context.repo.owner,&lt;/span&gt;
              &lt;span class="s"&gt;repo: context.repo.repo,&lt;/span&gt;
              &lt;span class="s"&gt;issue_number: prNumber,&lt;/span&gt;
            &lt;span class="s"&gt;});&lt;/span&gt;

            &lt;span class="s"&gt;const existing = comments.find(c =&amp;gt; c.body.includes(marker));&lt;/span&gt;
            &lt;span class="s"&gt;if (existing) {&lt;/span&gt;
              &lt;span class="s"&gt;await github.rest.issues.updateComment({&lt;/span&gt;
                &lt;span class="s"&gt;owner: context.repo.owner,&lt;/span&gt;
                &lt;span class="s"&gt;repo: context.repo.repo,&lt;/span&gt;
                &lt;span class="s"&gt;comment_id: existing.id,&lt;/span&gt;
                &lt;span class="s"&gt;body,&lt;/span&gt;
              &lt;span class="s"&gt;});&lt;/span&gt;
            &lt;span class="s"&gt;} else {&lt;/span&gt;
              &lt;span class="s"&gt;await github.rest.issues.createComment({&lt;/span&gt;
                &lt;span class="s"&gt;owner: context.repo.owner,&lt;/span&gt;
                &lt;span class="s"&gt;repo: context.repo.repo,&lt;/span&gt;
                &lt;span class="s"&gt;issue_number: prNumber,&lt;/span&gt;
                &lt;span class="s"&gt;body,&lt;/span&gt;
              &lt;span class="s"&gt;});&lt;/span&gt;
            &lt;span class="s"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# ─── Clean up preview when PR is closed (merged or abandoned) ────────────&lt;/span&gt;
  &lt;span class="na"&gt;cleanup-preview&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event.action == 'closed'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pr-preview-cleanup-${{ github.event.pull_request.number }}&lt;/span&gt;
      &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Remove preview directory from gh-pages branch&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pwsh&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Guard: if gh-pages doesn't exist yet there is nothing to clean up.&lt;/span&gt;
          &lt;span class="s"&gt;git ls-remote --exit-code --heads origin gh-pages *&amp;gt; $null&lt;/span&gt;
          &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
            &lt;span class="s"&gt;Write-Host "gh-pages branch does not exist - nothing to clean up."&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;

          &lt;span class="s"&gt;# Fetch and create a local tracking branch so 'git checkout' works.&lt;/span&gt;
          &lt;span class="s"&gt;git fetch origin gh-pages:gh-pages --depth=1&lt;/span&gt;
          &lt;span class="s"&gt;git checkout gh-pages&lt;/span&gt;

          &lt;span class="s"&gt;$prDir = "previews/pr-${{ github.event.pull_request.number }}"&lt;/span&gt;
          &lt;span class="s"&gt;if (Test-Path $prDir) {&lt;/span&gt;
            &lt;span class="s"&gt;git config user.name "github-actions[bot]"&lt;/span&gt;
            &lt;span class="s"&gt;git config user.email "github-actions[bot]@users.noreply.github.com"&lt;/span&gt;
            &lt;span class="s"&gt;git rm -r -f $prDir&lt;/span&gt;
            &lt;span class="s"&gt;git commit -m "Remove preview for PR #${{ github.event.pull_request.number }}"&lt;/span&gt;
            &lt;span class="s"&gt;git push --set-upstream origin gh-pages&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;else {&lt;/span&gt;
            &lt;span class="s"&gt;Write-Host "Preview directory '$prDir' not found - nothing to clean up."&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="main-branch-to-gh-pages-workflow"&gt;Main branch to gh-pages workflow&lt;/h4&gt;

&lt;p&gt;Below is an example workflow for deploying the production site to the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch when changes are merged into the main branch.
A key part of the workflow is preserving the &lt;code class="language-plaintext highlighter-rouge"&gt;previews&lt;/code&gt; subdirectory on the &lt;code class="language-plaintext highlighter-rouge"&gt;gh-pages&lt;/code&gt; branch, which contains the PR previews, so that they are not wiped out when the main production site is redeployed.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/deadlydog/Blog/blob/main/.github/workflows/deploy-site.yml" rel="noopener noreferrer"&gt;view the latest version of this deploy-site.yml workflow here&lt;/a&gt;.&lt;/p&gt;

&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Builds the Jekyll site from main and pushes it to the gh-pages branch,&lt;/span&gt;
&lt;span class="c1"&gt;# while preserving any PR preview directories that already exist there.&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy main to gh-pages&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby/setup-ruby@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ruby-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.3'&lt;/span&gt;
          &lt;span class="na"&gt;bundler-cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Jekyll site&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pwsh&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JEKYLL_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
          &lt;span class="na"&gt;JEKYLL_GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Override baseurl so the github-pages gem does not inject the&lt;/span&gt;
          &lt;span class="s"&gt;# repository name (/Blog) as the baseurl, which would break asset paths.&lt;/span&gt;
          &lt;span class="s"&gt;@"&lt;/span&gt;
          &lt;span class="s"&gt;url: "https://blog.danskingdom.com"&lt;/span&gt;
          &lt;span class="s"&gt;baseurl: ""&lt;/span&gt;
          &lt;span class="s"&gt;"@ | Set-Content -Path /tmp/_config_deploy.yml&lt;/span&gt;

          &lt;span class="s"&gt;bundle exec jekyll build --config _config.yml,/tmp/_config_deploy.yml&lt;/span&gt;

      &lt;span class="c1"&gt;# Preserve any PR preview directories that already live on gh-pages so&lt;/span&gt;
      &lt;span class="c1"&gt;# that a main-site redeploy does not wipe them out.&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore previews directories from gh-pages branch (if present)&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pwsh&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;git ls-remote --exit-code --heads origin gh-pages *&amp;gt; $null&lt;/span&gt;
          &lt;span class="s"&gt;if ($LASTEXITCODE -ne 0) {&lt;/span&gt;
            &lt;span class="s"&gt;Write-Host "gh-pages branch does not exist yet - skipping restore."&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;

          &lt;span class="s"&gt;git fetch origin gh-pages --depth=1&lt;/span&gt;

          &lt;span class="s"&gt;# Copy the previews tree into the freshly built _site so the&lt;/span&gt;
          &lt;span class="s"&gt;# subsequent deploy action includes it in the push.&lt;/span&gt;
          &lt;span class="s"&gt;git checkout origin/gh-pages -- previews/ 2&amp;gt;$null&lt;/span&gt;
          &lt;span class="s"&gt;if ($LASTEXITCODE -eq 0) {&lt;/span&gt;
            &lt;span class="s"&gt;Copy-Item -Recurse -Force previews _site/previews&lt;/span&gt;
            &lt;span class="s"&gt;Remove-Item -Recurse -Force previews&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;else {&lt;/span&gt;
            &lt;span class="s"&gt;Write-Host "No previews directory on gh-pages yet - skipping restore."&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to gh-pages branch&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peaceiris/actions-gh-pages@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;publish_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./_site&lt;/span&gt;
          &lt;span class="na"&gt;cname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blog.danskingdom.com&lt;/span&gt;
          &lt;span class="na"&gt;commit_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deploy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;site&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.sha&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="update-robotstxt-to-ignore-preview-sites"&gt;Update robots.txt to ignore preview sites&lt;/h3&gt;

&lt;p&gt;One optional, but recommended step, is to update your &lt;code class="language-plaintext highlighter-rouge"&gt;robots.txt&lt;/code&gt; file to disallow web crawlers from indexing the PR preview sites.
This will prevent the previews from showing up in search engine results, and users getting 404 errors when the previews are eventually removed after the PR is closed.&lt;/p&gt;

&lt;p&gt;In your site’s &lt;code class="language-plaintext highlighter-rouge"&gt;robots.txt&lt;/code&gt; file, add:&lt;/p&gt;

&lt;pre class="highlight"&gt;&lt;code&gt;# Prevent all web crawlers from indexing PR previews.
User-agent: *
Disallow: /previews/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the file is typically generated by your static site generator, you may need to include the lines that it would typically generate, such as the link to your sitemap file.
You can &lt;a href="https://github.com/deadlydog/Blog/blob/main/robots.txt" rel="noopener noreferrer"&gt;view my site’s robots.txt file here&lt;/a&gt; as an example.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;By hosting PR previews in subdirectories on the same GitHub Pages site, you can have free and easy PR previews without needing to set up an external hosting service.&lt;/p&gt;

&lt;p&gt;I’ve shown how to configure your GitHub repo, and provided example GitHub Actions workflows to automate the deployment of PR previews and the main site.&lt;/p&gt;

&lt;p&gt;I hope you found this helpful, and that it inspires you to set up PR previews for your own GitHub Pages sites!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>githubpages</category>
    </item>
    <item>
      <title>The part of the SDLC nobody talks about, and many companies don’t do properly</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/the-part-of-the-sdlc-nobody-talks-about-and-many-companies-dont-do-properly-1621</link>
      <guid>https://forem.com/deadlydog/the-part-of-the-sdlc-nobody-talks-about-and-many-companies-dont-do-properly-1621</guid>
      <description>&lt;p&gt;The most neglected phase of the software development lifecycle (SDLC) is often the decommissioning phase. An app has reached end-of-life, users are no longer using it, and everything should be shut down and deleted. It seems straightforward enough, but many companies overlook this phase, or do it poorly.&lt;/p&gt;

&lt;p&gt;Failing to properly decommission services can lead to significant costs, both obvious and hidden.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unused services and resources are costly in many ways, including money, creating noise in monitoring and alerting systems, adding mental overhead for teams, and potentially creating security vulnerabilities.&lt;/li&gt;
&lt;li&gt;It’s best to decommission services as soon as you know they are no longer needed.&lt;/li&gt;
&lt;li&gt;It’s important to have a clear process and checklist for decommissioning services, to ensure all of the resources get cleaned up properly and nothing gets missed. 

&lt;ul&gt;
&lt;li&gt;This article provides a starting process and checklist that you can build off of.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Decommissioning a service is not free, but the costs of not doing it properly are much higher in the long run.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Zombie resources
&lt;/h2&gt;

&lt;p&gt;If teams are diligent, they’ll remember to remove all of the resources their application used. However, it’s common for teams to clean up some resources, but forget others. The resources left behind and not doing anything are referred to as “zombie resources”.&lt;/p&gt;

&lt;p&gt;For example, teams may delete the app service, but forget to delete the database, or the backups. They might remove their application from a Virtual Machine (VM), but leave the VM running. They might delete the app, but forget to remove the DNS records, load balancer rules, or monitoring alerts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://n2ws.com/blog/cloud-computing-statistics" rel="noopener noreferrer"&gt;Recent statistics&lt;/a&gt; show that roughly 30% of cloud spend is wasted on unused or underutilized resources. The cost of these zombie resources can really add up over time, especially if teams just keep adding more of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why services remain running after they are no longer needed
&lt;/h2&gt;

&lt;p&gt;It’s easy to understand why people rush through the decommissioning phase and do not do it properly, or why sometimes it gets skipped altogether:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product and development teams want to focus on creating new features and fixing bugs; things that add value and bring in more revenue. Deleting things may not feel feel like it adds value, so it may get deprioritized.&lt;/li&gt;
&lt;li&gt;Org restructures may result in services being handed off to different teams, and the new team may not even realize the service exists.&lt;/li&gt;
&lt;li&gt;Sometimes a solo developer is responsible for a service, and when they leave the company, the service gets forgotten about.&lt;/li&gt;
&lt;li&gt;Developers will often spin up sandbox environments for testing and experimentation, and then forget to clean them up when they are done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many other reasons, but these are the common ones I’ve seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obvious costs of not decommissioning properly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o1kj7a45nnjfscn9bhq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8o1kj7a45nnjfscn9bhq.jpeg" alt="Forgotten cloud service makes man poor" width="400" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have a cloud service that is no longer in use, but still running, you are likely paying monthly for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The compute resources (VMs, containers, app services, functions/lambdas, etc.)&lt;/li&gt;
&lt;li&gt;Storage costs (databases, file storage, backups, etc.)&lt;/li&gt;
&lt;li&gt;Networking costs (data transfer, load balancers, etc.)&lt;/li&gt;
&lt;li&gt;Software licensing costs (per user or per instance licenses)&lt;/li&gt;
&lt;li&gt;Monitoring and alerting costs (pay per node, or ingestion rates for logs and metrics)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These monetary costs can be easy to identify, but only if you think to go looking for them. Some companies have a dedicated FinOps team whose job is to identify and eliminate these kinds of wasteful expenses. Many companies don’t though.&lt;/p&gt;

&lt;p&gt;Often times cloud costs are lumped together into one single number, rather than broken down by department, project, or team. It’s not always easy for a dev team to identify which costs are theirs and know how much they are spending, especially if they are not organizing their resources properly or using tags/labels.&lt;/p&gt;

&lt;p&gt;Sometimes the only people who even see the costs are the finance team when paying the bill. They won’t have the context to know if the amount is reasonable or not; they’ll just pay it.&lt;/p&gt;

&lt;p&gt;Even if the workloads are all on-premises, there are still potential monetary costs associated with keeping unused services around. They take up compute and storage resources that could be used for other things. Infrastructure teams may think they need to purchase additional hardware sooner than they actually do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hidden costs of not decommissioning properly
&lt;/h2&gt;

&lt;p&gt;Aside from the monetary hosting costs, there are hidden costs of not decommissioning apps, or only partially decommissioning them, that can be just as expensive.&lt;/p&gt;

&lt;h3&gt;
  
  
  System load and performance costs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You might have a cron job or processor service running that’s no longer necessary, making unnecessary requests, generating logs, and putting additional load on the system.&lt;/li&gt;
&lt;li&gt;The service might be a noisy neighbour that causes performance issues for other services still in use on the same infrastructure.&lt;/li&gt;
&lt;li&gt;It can create noise in monitoring and alerting systems, making it harder to identify real issues and lead to alert fatigue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mental costs of keeping old services around
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It creates a larger inventory of things to keep track of, creating additional mental overhead and cognitive load for teams.&lt;/li&gt;
&lt;li&gt;It can be worrying to have a bunch of unknown services running in your environment, especially if you don’t know what they are for or who is responsible for them. 

&lt;ul&gt;
&lt;li&gt;If they do eventually get assigned to a team, the team may feel stressed about having to be responsible for something they don’t understand or have any knowledge around.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;It can lead to a hesitancy of making changes for fear of breaking something, slowing progress.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  People time costs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You may need to have several meetings with different teams to figure out what a service is for, who is using it, and whether it can be safely decommissioned. Taking time out of people’s days is costly in terms of their hourly wages, but more importantly, it introduces context switching and lost productivity where they could have been working on something else.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updating dependencies (e.g. the .NET version or 3rd party libraries) can take a lot of time, especially if the service requires manual testing and deployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unnecessary components may be needlessly migrated during platform migrations. If you have a service that is no longer needed, but you don’t know it, time and effort will likely be spent migrating it to the new platform. This could be migrating it to a new hosting environment (e.g. Azure App Service to Kubernetes), or updating the app to send logs and metrics to a new monitoring platform (e.g. New Relic to Azure Monitor).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leads to inaccurate reports about what is in their current infrastructure, which may impact planning and decision making for things like capacity planning, budgeting, migrations, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automated jobs take longer to run (e.g. managing resources with scripts, IaC deployments, etc), so people wait longer for them to complete.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security costs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/azure/security/fundamentals/subdomain-takeover" rel="noopener noreferrer"&gt;Dangling DNS&lt;/a&gt; vulnerabilities when DNS is not decommissioned properly.&lt;/li&gt;
&lt;li&gt;Paying for security scanning and monitoring of unused services.&lt;/li&gt;
&lt;li&gt;More services that need to be patched to avoid vulnerabilities and attacks.&lt;/li&gt;
&lt;li&gt;It can create noise in security monitoring and vulnerability scanning, making it harder to identify real threats and leading to security fatigue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decommissioning safely
&lt;/h2&gt;

&lt;p&gt;If you’re able to decommission a service that you know is no longer used, you can often move straight to deleting the resources.&lt;/p&gt;

&lt;p&gt;If you’re not certain whether anything still relies on a service or component though, you likely want to take a few precautions before deleting it.&lt;/p&gt;

&lt;p&gt;A typical decommission flow might look something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review logging and monitoring data to ensure the service is no longer being used. 

&lt;ul&gt;
&lt;li&gt;If things are still calling the service, you should get those updated first to stop calling it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Notify the relevant teams and stakeholders of the decommission plans, including when it will be disabled.&lt;/li&gt;
&lt;li&gt;Disable any alerts associated with the service to avoid false paging or notifications.&lt;/li&gt;
&lt;li&gt;Disable the service in a way that is quick and easy to revert to perform a scream test (i.e. turn it off any see if anybody screams). 

&lt;ul&gt;
&lt;li&gt;e.g. Stop the Azure Web App, disable the cron job, update the k8s manifest, turn the Virtual Machine off, remove the DNS hostname, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Wait a period of time to ensure nothing blows up (e.g. a week), or perform a &lt;a href="https://en.wikipedia.org/wiki/Brownout_(software_engineering)" rel="noopener noreferrer"&gt;brownout schedule&lt;/a&gt;. 

&lt;ul&gt;
&lt;li&gt;Depending on the service, consider waiting longer if needed. e.g. Do month-end reports call the service? If so, leave it disabled over the month-end period before deleting it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Delete the service and all of the infrastructure supporting it.&lt;/li&gt;
&lt;li&gt;Delete any monitoring and alerting associated with the service.&lt;/li&gt;
&lt;li&gt;Backup and delete any data stores associated with the service.&lt;/li&gt;
&lt;li&gt;Delete or archive the code and any metadata associated with the service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You would want to perform the above steps in your non-production environments first, to minimize the risk of unexpected consequences when doing it in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decommission checklist
&lt;/h2&gt;

&lt;p&gt;Ideally you have everything defined in a central place as infrastructure as code; this makes finding and deleting everything easy.&lt;/p&gt;

&lt;p&gt;That’s often not a reality for many teams though. The next best thing is to have all of the infrastructure components documented somewhere, such as docs in the app’s git repo.&lt;/p&gt;

&lt;p&gt;Below is a non-exhaustive list of things to consider deleting when decommissioning a service, to hopefully ensure nothing is missed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and observability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Remove endpoints/nodes from availability checks (e.g. New Relic Synthetics, SolarWinds Orion, Azure Application Insights, etc.). 

&lt;ul&gt;
&lt;li&gt;Do this first so on-call personnel don’t get paged unnecessarily.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Delete related dashboards, alerts, SLIs, and SLOs (e.g. Application Insights, Hosted Graphite, SolarWinds, Honeycomb, Datadog, etc.).&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Infrastructure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Delete compute resources, such as Web Apps, Cloud Services, Functions/Lambas, Virtual Machines, etc.&lt;/li&gt;
&lt;li&gt;Delete empty App Service Plans and/or shuffle them if they are unbalanced after deleting a Web App.&lt;/li&gt;
&lt;li&gt;Delete empty resource groups and subscriptions.&lt;/li&gt;
&lt;li&gt;Delete Kubernetes resources and namespace (if applicable).&lt;/li&gt;
&lt;li&gt;Delete secret stores (e.g. Azure Key Vault).&lt;/li&gt;
&lt;li&gt;Delete service principals (e.g. Enterprise Application and Application Registrations from Azure Entra ID).&lt;/li&gt;
&lt;li&gt;Delete traffic manager profiles, load balancer rules, and DNS records.&lt;/li&gt;
&lt;li&gt;Delete any related API Management resources (e.g. Azure API Management).&lt;/li&gt;
&lt;li&gt;Remove integrations with any other 3rd party services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Backup (if necessary) and delete databases and data stores (e.g. Azure storage accounts, SQL databases, Redis caches, Elasticsearch, etc.).&lt;/li&gt;
&lt;li&gt;Delete service bus queues, topics, and subscribers.&lt;/li&gt;
&lt;li&gt;Delete CDN endpoints (e.g. Azure CDN, Azure Front Door)&lt;/li&gt;
&lt;li&gt;Delete API keys from 3rd party services (e.g. SendGrid, Honeycomb, Azure DevOps)&lt;/li&gt;
&lt;li&gt;Remove Active Directory groups and users related to the service&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Builds, deployments, repositories, and documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Archive, disable, or delete build and deployment pipelines.&lt;/li&gt;
&lt;li&gt;Update the git repo ReadMe to mention the service is now decommissioned, and archive the git repository.&lt;/li&gt;
&lt;li&gt;Archive or delete any wiki pages related to the service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Decommissioning a service is not free; it takes time and effort to do it properly. The longer you leave it though, the more it will cost you and your company.&lt;/p&gt;

&lt;p&gt;When people don’t know what a service is for, they will be hesitant to change or remove it, which can lead to it being left around indefinitely and repeatedly incurring the above mentioned costs. This is true not only for entire apps or systems, but also for individual components and resources. I have seen zombie services more than 15 years old still running in production!&lt;/p&gt;

&lt;p&gt;If you are unsure of what a service is for and whether it is still being used, it can take a lot of time to investigate and confirm that it is safe to decommission. So it’s important to invest the time upfront and decommission it as soon as you know it is no longer needed.&lt;/p&gt;

&lt;p&gt;The best way to ensure all parts of a service get decommissioned properly is to have a clear process and checklist for doing so. I’ve presented a starting checklist that you can build off of, but it should be customized to fit your company’s processes and infrastructure.&lt;/p&gt;

&lt;p&gt;It’s unlikely that the checklist will be perfect on the first try, so be sure to continually update it as you learn from each decommissioning experience.&lt;/p&gt;

&lt;p&gt;I hope this article has encouraged you to think about the decommissioning phase of the SDLC, and to hopefully save you and your company from the costs of neglecting it.&lt;/p&gt;

&lt;p&gt;Happy decommissioning!&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Easily capture terminal output from users you help remotely</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Sun, 01 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/easily-capture-terminal-output-from-users-you-help-remotely-2p9n</link>
      <guid>https://forem.com/deadlydog/easily-capture-terminal-output-from-users-you-help-remotely-2p9n</guid>
      <description>&lt;p&gt;When helping others troubleshoot issues with their PC, you often need to see the output of commands they run. The easiest way to do this is to synchronously video call and screen share, but that’s not always possible.&lt;/p&gt;

&lt;p&gt;Below are some alternative asynchronous options for seeing the output of commands they run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Print Screen
&lt;/h2&gt;

&lt;p&gt;An easy option is to use the PrtSc (or Print Screen) key to capture the whole screen, or Alt + PrtSc to capture just the active window.&lt;/p&gt;

&lt;p&gt;This will copy the screenshot to their clipboard, which they can then paste into a Slack or Teams message or email.&lt;/p&gt;

&lt;p&gt;A screenshot is nice and easy, unless the entire output does not fit nicely in a single image, or if you want to be able to copy and paste the output yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminal command options
&lt;/h2&gt;

&lt;p&gt;To get the output of a command, you can ask them to run the command and copy the output to the clipboard.&lt;/p&gt;

&lt;p&gt;Manually copying the output is not always easy though, as many terminals use block selection which is unintuitive and makes it hard to copy the entire output, especially if the output is long and requires scrolling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6j79lngozq6amxy9iqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6j79lngozq6amxy9iqw.png" alt="Example of block selection" width="576" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead, we can simply adjust the command they are running to automatically copy the output to the clipboard or save it to a file, which they can then send to you via Slack, Teams, email, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture command output to the clipboard
&lt;/h3&gt;

&lt;p&gt;Simply append &lt;code&gt;| clip&lt;/code&gt; to the end of the command to copy the output to the clipboard, preserving the formatting.&lt;/p&gt;

&lt;p&gt;For example, instead of just running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Process

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Process | clip

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for the output of any command or executable, not just PowerShell commands. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dsregcmd /status | clip

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works in both PowerShell and the old-school Command Prompt.&lt;/p&gt;

&lt;p&gt;In PowerShell, &lt;code&gt;clip&lt;/code&gt; is the alias for &lt;code&gt;Set-Clipboard&lt;/code&gt;, which is a built-in PowerShell cmdlet that copies input to the clipboard.&lt;/p&gt;

&lt;p&gt;There is also a &lt;code&gt;clip.exe&lt;/code&gt; executable too, so if you are in WSL or Git Bash, you can use &lt;code&gt;clip.exe&lt;/code&gt; to copy the output to the Windows clipboard. e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps | clip.exe

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;clip.exe&lt;/code&gt; will even work in PowerShell and Command Prompt, so you can always use it in place of &lt;code&gt;clip&lt;/code&gt; if you like.&lt;/p&gt;

&lt;p&gt;A downside to this method is that the output is not visible in the terminal; it is only copied to the clipboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture command output to a file
&lt;/h3&gt;

&lt;p&gt;You can use the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operator to redirect the output of a command to a file. e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Process &amp;gt;&amp;gt; C:\temp\output.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In PowerShell, the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operator is an alias for &lt;code&gt;Out-File&lt;/code&gt;, which is a built-in PowerShell cmdlet that writes output to a text file. e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Process | Out-File -FilePath C:\temp\output.txt -Append

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operator works in other shells too, like Command Prompt and Bash, so it’s a good one to know.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operator will append the output to the file if it already exists, while using just &lt;code&gt;&amp;gt;&lt;/code&gt; will overwrite the file if it already exists. Using &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; allows you to run multiple commands and capture all of the output to the same file.&lt;/p&gt;

&lt;p&gt;A downside of this approach is the output is not written to the terminal; it is only written to the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture command output to both the terminal and a file
&lt;/h3&gt;

&lt;p&gt;We can use &lt;code&gt;Tee&lt;/code&gt; to see the output in the terminal and save it to a file. e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Process | Tee C:\temp\output.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will write the output to both the terminal and the file.&lt;/p&gt;

&lt;p&gt;In PowerShell, &lt;code&gt;Tee&lt;/code&gt; is an alias for &lt;code&gt;Tee-Object&lt;/code&gt;. The full command syntax could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Process | Tee-Object -FilePath C:\temp\output.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tee&lt;/code&gt; exists in other shells as well like Bash, so using the shorthand syntax will work pretty much everywhere. e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps | tee output.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start a transcript to capture all output from the terminal session
&lt;/h3&gt;

&lt;p&gt;PowerShell has a built-in transcript feature that can capture all output from a terminal session and save it to a file. This is great if you want to capture the output of multiple commands.&lt;/p&gt;

&lt;p&gt;To start a transcript, simply run the &lt;code&gt;Start-Transcript&lt;/code&gt; cmdlet and specify the path to the file where you want to save the transcript. Then run the various commands you want to capture the output of. When you’re done, run the &lt;code&gt;Stop-Transcript&lt;/code&gt; cmdlet to stop the transcript and save the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Start-Transcript -Path C:\temp\transcript.txt

# Run various commands here
Get-Process
Get-Service
dsregcmd /status

Stop-Transcript

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transcript text file can then be sent to you via Slack, Teams, email, etc.&lt;/p&gt;

&lt;p&gt;This method only works for PowerShell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While screen sharing is often the best way to interactively help someone and see the output of commands in real time, it’s not always possible. Taking screenshots is easy and effective, except in the case of long outputs that don’t fit on a single screen, or when you want to be able to copy and paste the output yourself.&lt;/p&gt;

&lt;p&gt;Luckily, there are several easy options for capturing terminal output to a file or the clipboard, which can then be shared via Slack, Teams, email, etc.&lt;/p&gt;

&lt;p&gt;I hope you’ve found this post informative and it helps you when working with others remotely!&lt;/p&gt;

&lt;p&gt;Happy helping and troubleshooting! 😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2bbar4gm0skxp7kl7zo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2bbar4gm0skxp7kl7zo.png" alt="Post thumbnail image" width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Colour your kubectl output and type less</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/colour-your-kubectl-output-and-type-less-518g</link>
      <guid>https://forem.com/deadlydog/colour-your-kubectl-output-and-type-less-518g</guid>
      <description>&lt;p&gt;If you work with Kubernetes on the command line using &lt;code&gt;kubectl&lt;/code&gt;, you will appreciate &lt;a href="https://kubecolor.github.io/" rel="noopener noreferrer"&gt;kubecolor&lt;/a&gt;. Kubecolor is a small utility that adds colour to your &lt;code&gt;kubectl&lt;/code&gt; output, making it easier to read and understand at a glance.&lt;/p&gt;

&lt;p&gt;Normally when you run a &lt;code&gt;kubectl&lt;/code&gt; command, you get back a wall of boring grey text. With kubecolor, you get informative, colourful output like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bb7nr7ge7fp1pkjgutg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bb7nr7ge7fp1pkjgutg.png" alt="Kubecolor sample output 1" width="509" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forkyeyi8el3x3svaz6kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forkyeyi8el3x3svaz6kq.png" alt="Kubecolor sample output 2" width="735" height="889"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I especially appreciate how errors are highlighted red, making them easy to spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubecolor installation
&lt;/h2&gt;

&lt;p&gt;Kubecolor is cross-platform, easy to install, and has great &lt;a href="https://kubecolor.github.io/setup/install/" rel="noopener noreferrer"&gt;installation documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, installing it on Windows is as easy as running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;winget install --id Kubecolor.kubecolor

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup to use kubecolor by default
&lt;/h2&gt;

&lt;p&gt;The Kubecolor documentation also has &lt;a href="https://kubecolor.github.io/setup/shells/powershell/" rel="noopener noreferrer"&gt;great setup instructions&lt;/a&gt; for many shells, including PowerShell.&lt;/p&gt;

&lt;p&gt;You can typically set up an alias so that anytime you run &lt;code&gt;kubectl&lt;/code&gt;, it automatically uses kubecolor. Better yet, instead of having to type &lt;code&gt;kubectl&lt;/code&gt; every time, just type &lt;code&gt;k&lt;/code&gt; instead!&lt;/p&gt;

&lt;p&gt;Here’s how you can set that up in PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set-Alias -Name kubectl -Value kubecolor
Set-Alias -Name k -Value kubectl

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run &lt;code&gt;k get pods&lt;/code&gt; and see colourful output by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup kubectl command and parameter completions
&lt;/h2&gt;

&lt;p&gt;The documentation also has instructions for setting up &lt;a href="https://kubecolor.github.io/setup/shells/powershell/" rel="noopener noreferrer"&gt;kubectl completions&lt;/a&gt; in many shells, which is super handy.&lt;/p&gt;

&lt;p&gt;This is how it can be done in PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl completion powershell | Out-String | Invoke-Expression
Register-ArgumentCompleter -CommandName 'k','kubectl','kubecolor' -ScriptBlock $__kubectlCompleterBlock

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can type &lt;code&gt;k get p&lt;/code&gt; and hit Tab and it will auto-complete. Continue hitting Tab to cycle through the available options, or use Ctrl + Space to see all available options.&lt;/p&gt;

&lt;p&gt;This works for both command names and parameter values, making it easier to discover &lt;code&gt;kubectl&lt;/code&gt; commands and custom resources in your cluster. Try typing each of the following and hitting Ctrl + Space. Note the trailing space at the end of each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k get&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k get pods -n&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automatic setup with PowerShell profile
&lt;/h2&gt;

&lt;p&gt;You don’t want to have to run the setup commands every time you open PowerShell though. You can use your PowerShell profile to run the code automatically every time PowerShell starts.&lt;/p&gt;

&lt;p&gt;In PowerShell, you can find the path to your PowerShell profile by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$PROFILE

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To open the file in Notepad, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;notepad $PROFILE

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the file if it doesn’t exist, and add the following code to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# If kubecolor is installed, alias kubectl to use it.
if (Get-Command kubecolor -ErrorAction SilentlyContinue)
{
  Set-Alias -Name kubectl -Value kubecolor
}

# If kubectl is installed, register the argument completions for kubectl and its aliases.
if (Get-Command kubectl -ErrorAction SilentlyContinue)
{
  Set-Alias -Name k -Value kubectl
  kubectl completion powershell | Out-String | Invoke-Expression
  Register-ArgumentCompleter -CommandName 'k','kubectl','kubecolor' -ScriptBlock $__kubectlCompleterBlock
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it!&lt;/p&gt;

&lt;p&gt;To test it out, open a new PowerShell window and run &lt;code&gt;k version&lt;/code&gt; or &lt;code&gt;k get pods&lt;/code&gt;. You should see beautiful, colourful output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Colour themes
&lt;/h2&gt;

&lt;p&gt;Kubecolor comes with several built-in colour themes, including light and dark themes. It even allows you to create your own custom themes. See &lt;a href="https://kubecolor.github.io/customizing/themes/" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt; for more info.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So now you get beautiful, colourful &lt;code&gt;kubectl&lt;/code&gt; output that is easier to read and understand at a glance. You also get to type less by using the &lt;code&gt;k&lt;/code&gt; alias, and improve discoverability with command and parameter completions.&lt;/p&gt;

&lt;p&gt;I hope you found this useful.&lt;/p&gt;

&lt;p&gt;Happy Kuberneting!&lt;/p&gt;

&lt;p&gt;If you enjoyed this post, you might also like &lt;a href="https://blog.danskingdom.com/Update-your-terminal-prompt-and-font-in-Windows-Terminal-and-VS-Code-and-Visual-Studio/" rel="noopener noreferrer"&gt;this post on using Oh My Posh and updating your terminal to use different fonts&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>powershell</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Handy Windows Command Prompt commands</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Sun, 25 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/handy-windows-command-prompt-commands-2nhh</link>
      <guid>https://forem.com/deadlydog/handy-windows-command-prompt-commands-2nhh</guid>
      <description>&lt;p&gt;Below are some handy Windows Command Prompt commands that can help you troubleshoot and manage your system more effectively.&lt;/p&gt;

&lt;p&gt;For commands that produce a lot of output, consider redirecting the output to a text file for easier reading. You can do this by appending &lt;code&gt;&amp;gt; filename.txt&lt;/code&gt; to the command. Another option is appending &lt;code&gt;| clip&lt;/code&gt; when using PowerShell to copy the output directly to the clipboard so you can paste it in a text editor.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;chkdsk&lt;/td&gt;
&lt;td&gt;Checks the file system for logical and physical errors. Can fix some issues automatically using &lt;code&gt;/f&lt;/code&gt; or &lt;code&gt;/r&lt;/code&gt; parameters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dism /Online /Cleanup-Image /RestoreHealth&lt;/td&gt;
&lt;td&gt;Repairs the Windows image, fixing corruption and other issues that may affect system stability.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpupdate /force&lt;/td&gt;
&lt;td&gt;Forces an immediate update of Group Policy settings. Useful for applying changes without waiting for the next automatic refresh.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ipconfig&lt;/td&gt;
&lt;td&gt;Displays the current network configuration, including IP address, subnet mask, and default gateway.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ipconfig /displaydns&lt;/td&gt;
&lt;td&gt;Shows the contents of the DNS resolver cache.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ipconfig /flushdns&lt;/td&gt;
&lt;td&gt;Clears the DNS resolver cache, which can help resolve certain connectivity issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;powercfg /batteryreport&lt;/td&gt;
&lt;td&gt;Generates a detailed battery report for laptops, showing past usage and battery health.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;netstat -anob&lt;/td&gt;
&lt;td&gt;Displays active network connections, their ports, and the associated app name and process ID (PID) using the port. Useful for identifying suspicious connections, and finding which application is using a particular port.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sfc /scannow&lt;/td&gt;
&lt;td&gt;Scans all protected system files and replaces corrupted files with a cached copy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;whoami&lt;/td&gt;
&lt;td&gt;Displays the current logged-in user name along with domain information.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;whoami /groups&lt;/td&gt;
&lt;td&gt;Lists all the groups the current user belongs to.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;winget upgrade&lt;/td&gt;
&lt;td&gt;Lists all installed applications that have available updates via Windows Package Manager.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;winget upgrade –all&lt;/td&gt;
&lt;td&gt;Upgrades all installed applications that have available updates via Windows Package Manager.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wmic product get name&lt;/td&gt;
&lt;td&gt;Lists all installed software on the system. Useful for inventory or troubleshooting purposes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wmiobject win32_winsat&lt;/td&gt;
&lt;td&gt;Displays the Windows System Assessment Tool (WinSAT) scores, which provides hardware performance scores to quickly see a PCs overall performance.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I intend to keep updating this list as I discover more useful commands. If you know of others, please let me know in the comments.&lt;/p&gt;

&lt;p&gt;If you like these, you might also like my &lt;a href="https://blog.danskingdom.com/Common-Run-commands-to-access-various-Windows-settings-and-apps/" rel="noopener noreferrer"&gt;Common Run commands to access various Windows settings and apps post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope you find these helpful. Happy computing!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6tuk8djxlkabtvz4v1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6tuk8djxlkabtvz4v1b.png" alt="Windows PowerShell command prompt stock image" width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>windows</category>
    </item>
    <item>
      <title>Logitech MX Master 4 vs 3s comparison by a 40 something year old</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Sat, 06 Dec 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/logitech-mx-master-4-vs-3s-comparison-by-a-40-something-year-old-37di</link>
      <guid>https://forem.com/deadlydog/logitech-mx-master-4-vs-3s-comparison-by-a-40-something-year-old-37di</guid>
      <description>&lt;p&gt;Ever since I got the &lt;a href="https://www.logitech.com/en-ca/shop/p/mx-master-3s" rel="noopener noreferrer"&gt;Logitech MX Master 3s&lt;/a&gt; in December 2023, it’s been my favourite mouse of all time, beating out my previous favourites from the 2000s, &lt;a href="https://www.reddit.com/r/MouseReview/comments/1egvuwg/in_light_of_logitechs_forever_mouse_my_mx510_i/" rel="noopener noreferrer"&gt;the Logitech MX510&lt;/a&gt; and later &lt;a href="https://www.amazon.co.uk/Logitech-MX518-Optical-Gaming-Mouse/dp/B0015R8M7U" rel="noopener noreferrer"&gt;the Logitech MX518&lt;/a&gt;, both discontinued (although there’s &lt;a href="https://www.amazon.ca/dp/B07P32PJZD" rel="noopener noreferrer"&gt;a newer reincarnated MX518 now&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;But this isn’t about those mice; it’s about the &lt;a href="https://www.logitech.com/en-ca/shop/p/mx-master-4" rel="noopener noreferrer"&gt;Logitech MX Master 4&lt;/a&gt;, which was released in October 2024, and how I found it compared to the MX Master 3s.&lt;/p&gt;

&lt;p&gt;This is not meant to be a comprehensive review; just my personal experience and what stood out to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I got the MX Master 3s
&lt;/h2&gt;

&lt;p&gt;I’ve been using the Logitech MX Master 3s for almost 2 years now. I got it because I started having wrist pain, and thought a more ergonomic mouse might help.&lt;/p&gt;

&lt;p&gt;I first tried &lt;a href="https://www.logitech.com/en-ca/shop/p/mx-vertical-ergonomic-mouse" rel="noopener noreferrer"&gt;the Logitech MX Vertical&lt;/a&gt;, but didn’t like how I had to move my entire arm to move the mouse. I then tried the MX Master 3s. It was super comfortable, had all of the essential buttons (left, right, middle, back, forward, and scroll wheel), as well as a thumb wheel and gesture thumb button. I loved that all of the buttons were programmable too, via the Logi Options+ app. The thumb gesture button allows you to click it, as well as hold it and move the mouse up/down/left/right to trigger actions, so I have mine configured to control my media (play/pause, volume up/down, next/previous track).&lt;/p&gt;

&lt;p&gt;After using it for a couple weeks my wrist pain went away, and I had a new favourite mouse!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let my pain be a lesson kids. Invest in an ergonomic mouse and keyboard now, &lt;strong&gt;before&lt;/strong&gt; you develop problems!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A common problem with the MX Master 3s
&lt;/h2&gt;

&lt;p&gt;I love the MX Master 3s. After almost 2 years though, it started having flaky issues with left button clicks. Sometimes it would not register single clicks, or would double-click when I only clicked once. It also made dragging-and-dropping very difficult, as most times it would release the drag before I let go of the button.&lt;/p&gt;

&lt;p&gt;After some research, I found that this is a common issue with the MX Master 3s and the typical fix is to replace the button switch (as shown in &lt;a href="https://www.youtube.com/watch?v=yTrwT5ddfcM" rel="noopener noreferrer"&gt;this YouTube video&lt;/a&gt;), which requires taking the mouse apart and soldering. While I like to imagine myself as a DIY guy, I’ve never soldered before and don’t have the tools on hand.&lt;/p&gt;

&lt;p&gt;Instead, I took the mouse apart and applied a couple pieces of tape to the mouse casing above the switch to increase the tension on it (also shown in the above YouTube video). This helped a bit and the issues were less frequent, but didn’t totally fix the issue. A week later I also applied a drop of WD-40 to the switch, &lt;a href="https://www.reddit.com/r/logitech/comments/1g0c8kd/mx_master_3s_mouse_switch_replacement/" rel="noopener noreferrer"&gt;as mentioned on Reddit&lt;/a&gt;. This has my 3s working normally again, at least for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying out the MX Master 4
&lt;/h2&gt;

&lt;p&gt;Since the MX Master 4 had just come out, I figured it was a good time to upgrade, as I wasn’t sure how long my temporary fix to my 3s would last.&lt;/p&gt;

&lt;p&gt;Unfortunately, it sounds like the MX Master 4 uses the same button switches as the 3s, so they will likely have the same issue eventually. Time will tell.&lt;/p&gt;

&lt;p&gt;Below is a breakdown of how I found the MX Master 4 compared to the 3s.&lt;/p&gt;

&lt;p&gt;✅ MX Master 4 &lt;strong&gt;Pros&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I like the thumb wheel better. It sits closer to the tip of my thumb, making it more comfortable to use.&lt;/li&gt;
&lt;li&gt;Has an extra programmable gesture button in front of the forward button, which was a compelling reason to upgrade.&lt;/li&gt;
&lt;li&gt;The haptic feedback (vibration) is neat, but not really useful. The only time I notice it is when waking the mouse and using the Actions Ring. Right now very few apps make use of it, but that may change in the future.&lt;/li&gt;
&lt;li&gt;Screws to take it apart do not require removing the feet pads, like the 3s. 

&lt;ul&gt;
&lt;li&gt;Uses 6 phillips screws, which is nice. The 3s uses 5 phillips screws and 1 torx screw, which was annoying as I had to hunt down a torx screwdriver.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Feels very similar to the MX Master 3s in terms of shape, size, and button layout, and uses the same Logi Options+ app, so upgrading felt smooth.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;❌ MX Master 4 &lt;strong&gt;Cons&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It feels quite a bit heavier than the 3s. The specs say it’s only 9 grams heavier (150g vs 141g), but it feels like more. 

&lt;ul&gt;
&lt;li&gt;I was getting wrist pain a couple years back, and the 3s alleviated that for nearly 2 years. After using the 4 for a little over a week, the wrist pain returned.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The thumb button is triggered by pressing &lt;em&gt;down&lt;/em&gt; or &lt;em&gt;in&lt;/em&gt;, so I often press it in by accident when adjusting the mouse new position on my desk, such as when changing my sitting position. 

&lt;ul&gt;
&lt;li&gt;In the 3s, you have to press the thumb button &lt;em&gt;down&lt;/em&gt;, allowing you to easily pick up the mouse (by squeezing your thumb in) without accidentally pressing the button.&lt;/li&gt;
&lt;li&gt;The 4 is both heavier and smoother than the 3s. Even with the textured bumps on the thumb button, it’s quite slippery, making the thumb slide when picking it up and forcing you to grip it tighter, leading to accidental presses.&lt;/li&gt;
&lt;li&gt;I found this mentioned in several other online reviews as well, so I know it’s not just me.&lt;/li&gt;
&lt;li&gt;I change the thumb button to media controls (the default action is the Actions Ring), so more than once I accidentally pressed the thumb button while adjusting the mouse position on my desk and started playing music while on a video call (embarrassing! 🫠).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The gesture button is so far forward it’s a bit hard to reach with my thumb. 

&lt;ul&gt;
&lt;li&gt;It’s not a huge issue, but I noticed myself having to consciously think about reaching for it. That could change and become muscle memory over time though.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The scroll wheel button to switch between ratchet and free-spin modes makes a noticeable click sound, where it is silent on the 3s. This doesn’t really bother me though; just something I noticed.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The feel of the MX 4 in my hand is very similar to the 3s. The 3s is a bit grippier due to the rubberized surface, while the MX 4 has a smoother plastic finish. I didn’t mind the feel, but considering the MX 4 is heavier, it could have benefitted from the grippier surface. The shape is nearly identical, with only very minor differences.&lt;/p&gt;

&lt;p&gt;If I didn’t feel them side-by-side, it would be hard to tell which is which, aside from the weight difference and the feel of the thumb button. If you like the feel of the MX 3s, you’ll likely like the MX 4 as well.&lt;/p&gt;

&lt;p&gt;I’ll note that I use Bluetooth for both, so I can’t comment on the performance of the included Logi Bolt USB receiver. I can say though that the 3s uses a USB-A port, while the 4 uses USB-C, so you may need to consider how many open ports you have.&lt;/p&gt;

&lt;p&gt;Also, while I have used both mice for video games, I don’t play competitive or fast-paced games where low millisecond response times make a difference. Both mice perform completely fine for my casual gaming needs. Others have commented that if you need high polling rates, neither of these mice are ideal and you should invest in a proper gaming mouse.&lt;/p&gt;

&lt;p&gt;Here’s a few pics to visually compare the two, with the 3s being the black one (bottom/left) and the 4 the graphite (grey) one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkih6fiitmc3hx9xdqxwe.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkih6fiitmc3hx9xdqxwe.jpeg" alt="Top view" width="768" height="1024"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnypjztysk6qjq40vw9e8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnypjztysk6qjq40vw9e8.jpeg" alt="Back view" width="800" height="600"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1m7xk2abe60ozuhdl4e.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1m7xk2abe60ozuhdl4e.jpeg" alt="Left side view" width="800" height="600"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08di785jc1oqcifz5nnw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08di785jc1oqcifz5nnw.jpeg" alt="Right side view" width="800" height="600"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4lmpsq3c30e5uytp4zj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4lmpsq3c30e5uytp4zj.jpeg" alt="Side profile view" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While I quite liked the MX Master 4, especially for the extra programmable button, the wrist pain due to increased weight and accidental thumb button presses were a deal-breaker for me.&lt;/p&gt;

&lt;p&gt;I ended up returning my MX Master 4 and will likely be ordering another 3s if the click issues return. If I was still in my 20s and hadn’t developed wrist pain, I likely would have kept the MX 4.&lt;/p&gt;

&lt;p&gt;If Logitech releases a followup version that’s a bit lighter and addresses the accidental thumb button presses, I would try it out again.&lt;/p&gt;

&lt;p&gt;I don’t often do product reviews or comparisons, so if you enjoyed this leave a comment and let me know!&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>review</category>
    </item>
    <item>
      <title>Should observability replace custom error messages?</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Sun, 19 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/should-observability-replace-custom-error-messages-bnp</link>
      <guid>https://forem.com/deadlydog/should-observability-replace-custom-error-messages-bnp</guid>
      <description>&lt;p&gt;Open Telemetry (OTEL) and observability tools have come a long way in recent years. They can provide a wealth of information about what your application was doing when an error occurred. But can they replace good custom error messages that provide additional context about what went wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Dave Callan, a fellow Microsoft MVP, recently &lt;a href="https://www.linkedin.com/posts/davidcallan_net-devs-when-you-expect-exactly-1-item-activity-7382327791356334080-PgYc/" rel="noopener noreferrer"&gt;asked on LinkedIn&lt;/a&gt; if .NET developers prefer to use &lt;code&gt;.First()&lt;/code&gt;, &lt;code&gt;.FirstOrDefault()&lt;/code&gt;, &lt;code&gt;.Single()&lt;/code&gt;, or &lt;code&gt;.SingleOrDefault()&lt;/code&gt; when they expect exactly one item in a collection.&lt;/p&gt;

&lt;p&gt;I’ve written about this very subject, so I commented with a link to &lt;a href="https://blog.danskingdom.com/First-Single-and-SingleOrDefault-in-dotnet-are-harmful/" rel="noopener noreferrer"&gt;my blog post&lt;/a&gt; where I state that I prefer to always use &lt;code&gt;.FirstOrDefault()&lt;/code&gt; because it allows me to provide a descriptive custom error message, which can make troubleshooting much easier.&lt;a href="https://www.linkedin.com/feed/update/urn:li:ugcPost:7382327790437814272?commentUrn=urn%3Ali%3Acomment%3A%28ugcPost%3A7382327790437814272%2C7382838797274845184%29&amp;amp;replyUrn=urn%3Ali%3Acomment%3A%28ugcPost%3A7382327790437814272%2C7383115784740417536%29&amp;amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287382838797274845184%2Curn%3Ali%3AugcPost%3A7382327790437814272%29&amp;amp;dashReplyUrn=urn%3Ali%3Afsd_comment%3A%287383115784740417536%2Curn%3Ali%3AugcPost%3A7382327790437814272%29" rel="noopener noreferrer"&gt;Dave replied&lt;/a&gt; that he prefers to use &lt;code&gt;.Single()&lt;/code&gt;, even though it gives a generic error message, and rely on observability tools to troubleshoot issues when they arise; especially for issues that “should never happen”.&lt;/p&gt;

&lt;p&gt;This got me thinking about whether observability tools can truly replace good custom error messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability is awesome
&lt;/h2&gt;

&lt;p&gt;If you’re not familiar with &lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; (OTEL), it’s a standard set of APIs for collecting metrics, traces, and logs from your application, giving you observability into its behavior. You create spans to track how long an operation takes, and add attributes to those spans to provide additional context. The attributes can be things specific to the operation, such as the user or items involved in the operation, or more general information, such as the environment, version of the application, or the operating system it’s running on.&lt;/p&gt;

&lt;p&gt;You then export the OTEL data into an observability tool, which allows you to visualize and query the data. This allows you to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which company, user, or region has the most failed requests this week?&lt;/li&gt;
&lt;li&gt;What is the average response time for requests to a specific endpoint? Which endpoints are the slowest?&lt;/li&gt;
&lt;li&gt;What do failing requests have in common? e.g. a specific user, company, server, region, browser, app version, etc.&lt;/li&gt;
&lt;li&gt;What were the headers, parameters, or payload of a specific request?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also allows for distributed tracing, letting you see how individual requests flow through your application and its dependencies (e.g. other services, database calls, etc.), allowing you to pinpoint where things went wrong or spot potential bottlenecks between components.&lt;/p&gt;

&lt;p&gt;There’s no question that adding OTEL instrumentation to your application can provide valuable insights into its behavior. It provides additional information, allowing you to troubleshoot issues and spot trends before they become problems. When possible, you should absolutely add OTEL instrumentation to your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should observability replace custom error messages though?
&lt;/h2&gt;

&lt;p&gt;To be clear, I’m not talking about simply catching an exception and rewriting it with a more user-friendly message. I’m talking about including specific data that may have led to the error, such as the parameter values that were used, the dataset that was being queried, or any other relevant context that could aid in troubleshooting.&lt;/p&gt;

&lt;p&gt;Here’s some typical code that would throw an exception with a generic error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var people = new List&amp;lt;string&amp;gt; { "Alfred Archer", "Billy Baller", "Billy Bob", "Cathy Carter" };
var name = "Zane";

// Throws generic exception: System.InvalidOperationException: Sequence contains no matching element
var zane = people.First(x =&amp;gt; x.StartsWith(name));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And modified code that provides a more descriptive custom error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var people = new List&amp;lt;string&amp;gt; { "Alfred Archer", "Billy Baller", "Billy Bob", "Cathy Carter" };
var name = "Zane";

try
{
    var zane = people.First(x =&amp;gt; x.StartsWith(name));
}
catch (InvalidOperationException ex)
{
    throw new PersonNotFoundException($"Could not find a person named '{name}' in the people list.", ex);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the custom error message provides the name of the person that was being searched for, which can be tremendously helpful when troubleshooting. It makes it easy to know the exact name, and verify there were no typos or unexpected characters. e.g. They searched for “ Zane “ (with spaces), or “Zone” instead of “Zane”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Be mindful to not include sensitive information in error messages, such as passwords or personal data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could have also added OTEL instrumentation to the code, creating a span for the operation and adding attributes for the name being searched for.&lt;/p&gt;

&lt;p&gt;Even if we had added OTEL instrumentation, I would argue that custom error messages are still valuable for a number of reasons:&lt;/p&gt;

&lt;h3&gt;
  
  
  Quicker troubleshooting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Custom error messages can provide immediate context about what went wrong, and can be surfaced in different ways: 

&lt;ul&gt;
&lt;li&gt;Shown in the observability tooling.&lt;/li&gt;
&lt;li&gt;Logged in application logs (local or remote), separate from the observability tooling.&lt;/li&gt;
&lt;li&gt;Displayed to the user, if appropriate.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Allows developers to identify problems faster, as they don’t need to query the observability tooling and follow breadcrumbs to find the relevant information.&lt;/li&gt;

&lt;li&gt;Potentially allows users to self-serve and resolve issues on their own, if the error message is displayed to them. 

&lt;ul&gt;
&lt;li&gt;If a user presses a button to submit a form and sees the error message “System.InvalidOperationException: Sequence contains more than one matching element”, they may not know what to do. However, if they see “Error: More than one user found with the email address ‘&lt;a href="mailto:me@example.com"&gt;me@example.com&lt;/a&gt;’”, they have a clearer understanding of the issue and can potentially resolve it themselves.&lt;/li&gt;
&lt;li&gt;Allowing users or support staff to resolve issues on their own saves both users and developers time, making everyone happier.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Do observability tools have the data you need?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Observability tooling relies on the data that is sent to it. If the necessary data is not being captured, it may not be available when you need it.&lt;/li&gt;
&lt;li&gt;Custom error messages can provide additional context only when its needed. 

&lt;ul&gt;
&lt;li&gt;For example, a custom error message may include the parameters that were used for a specific query, or the dataset that was being queried, whereas you may not want to log that information for every request in your observability tooling.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Do you have every component in the chain instrumented? 

&lt;ul&gt;
&lt;li&gt;If your application calls an external service or third-party library that is not instrumented, it may not have the necessary data to help troubleshoot an issue.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Budgets change, or are non-existent
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Observability tooling can be expensive, and not all organizations have the budget for it.&lt;/li&gt;
&lt;li&gt;Even if your organization has the budget now, it may not in the future.&lt;/li&gt;
&lt;li&gt;Many organizations implement sampling to reduce costs (e.g. only retain 20% of traces), which means you may not have the necessary data to troubleshoot a specific issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Access to observability tooling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Not all developers may have access to the observability tooling, especially in larger organizations, or where you pay per user. 

&lt;ul&gt;
&lt;li&gt;Does every team member have access to the tooling? What about support staff?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Team members change
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Do all team members know how to effectively use the observability tooling? 

&lt;ul&gt;
&lt;li&gt;Team members who are familiar with the tools may leave the organization.&lt;/li&gt;
&lt;li&gt;New team members may not be familiar with the observability tools, know where to find them, or even know they exist.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Observability tooling changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Organizations may change observability providers (e.g. New Relic to Datadog to Honeycomb to …), which means team members may need to learn new tools.&lt;/li&gt;
&lt;li&gt;Have all of the old apps and services been updated to send data to the new observability provider?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: I’m a huge fan of &lt;a href="https://www.honeycomb.io" rel="noopener noreferrer"&gt;Honeycomb.io&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The type of application matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Do users expect a desktop/mobile application to be sending observability data over the network?&lt;/li&gt;
&lt;li&gt;If not stored remotely, do users expect your app to store all of that observability data locally?&lt;/li&gt;
&lt;li&gt;Users may not want an application to send or store observability data at all, due to privacy, data usage, or performance concerns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It probably seems obvious that it’s preferable to include information in the error message that can help troubleshoot the issue. I can’t tell you though how many times I’ve been frustrated, both as a user and as a developer, by generic error messages that provide no context about what went wrong.&lt;/p&gt;

&lt;p&gt;I’m of the opinion that the more information you can provide when an error occurs, the better. There’s no doubt that observability can provide additional information that can help troubleshoot issues. However, there’s no guarantee that it will provide the same information that a well-crafted custom error message can provide, or that the observability data will be available when you need it.&lt;/p&gt;

&lt;p&gt;A developer spending an extra couple of minutes writing a custom error message that provides additional context can save hours, even days, of troubleshooting time later on. Even if it’s not an error message the end-user will see, it can still be tremendously helpful for developers and support staff.&lt;/p&gt;

&lt;p&gt;So the next time you’re performing an operation that may potentially throw an exception, consider whether catching and re-throwing the exception with additional context information may be beneficial.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>My favourite Windows shortcuts</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Wed, 24 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/my-favourite-windows-shortcuts-217m</link>
      <guid>https://forem.com/deadlydog/my-favourite-windows-shortcuts-217m</guid>
      <description>&lt;p&gt;Windows has a ton of keyboard shortcuts that can help you be more efficient. Below are some of my favourites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdirfjpcg9qtofqvtsujo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdirfjpcg9qtofqvtsujo.png" alt="Windows shortcuts cartoon image" width="150" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that Win is the Windows key.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you enjoy this post, check out my &lt;a href="https://blog.danskingdom.com/Windows-settings-I-change-after-a-clean-install/" rel="noopener noreferrer"&gt;Windows settings I change after a clean install&lt;/a&gt; post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Windows shortcuts
&lt;/h2&gt;

&lt;p&gt;You can use the following shortcuts regardless of the application you’re in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Alt + Tab: Cycle between open windows.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alt + Shift + Tab: Cycle between open windows in reverse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Up: Maximize the current window.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Down: Restore / Minimize the current window.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Left / Right: Snap the current window to the left or right side of the screen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Shift + Left / Right: Move the current window to the left or right monitor (only works with multiple monitors set up).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Alt + Left / Right / Up / Down: Snap the current window to a specific quadrant of the screen (Windows 11 only).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Tab: Open Task View to see all virtual desktops and all windows in the current virtual desktop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Ctrl + D: Create a new virtual desktop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + Ctrl + Left / Right: Switch between virtual desktops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + D: Minimize all windows; press again to restore all windows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + E: Open File Explorer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + R: Open the Run dialog.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + P: Open the Project menu (for connecting to external displays).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + I: Open Settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Win + L: Lock the computer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ctrl + Shift + Esc: Open Task Manager.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Win + A: Open the Action Center. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains controls for Wi-Fi, Bluetooth, Airplane mode, volume, brightness, and more.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Win + X: Open the Quick Link menu. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains links to many system tools, such as Installed Apps, Device Manager, Sign out, Shut down etc.&lt;/li&gt;
&lt;li&gt;I often use Win + X followed by A to open an elevated terminal window.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Win + Period, or Win + Semicolon: Open the emoji picker 🤩. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once in the emoji picker, type to search, use Shift + Arrow keys to navigate, Enter to insert the emoji, and Esc to close the picker.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Win + V: Open Clipboard history to paste things copied in the past. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s nice that this is built into Windows 10+, but I prefer to use &lt;a href="https://sabrogden.github.io/Ditto/" rel="noopener noreferrer"&gt;Ditto Clipboard Manager&lt;/a&gt; instead. See my &lt;a href="https://youtu.be/bBvKvJfWw2c?si=VCoUFqnJAV5VwnAn" rel="noopener noreferrer"&gt;YouTube video on using Ditto&lt;/a&gt;, and the &lt;a href="https://youtu.be/0fRL6PFGckM?si=wILKOwQg1msrnVVN" rel="noopener noreferrer"&gt;followup video showing more features&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Win + Shift + S: Take a screenshot of part of the screen and edit it with the Snipping Tool. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I prefer to use &lt;a href="https://getsharex.com" rel="noopener noreferrer"&gt;ShareX&lt;/a&gt; instead, which has many more features.&lt;/li&gt;
&lt;li&gt;Windows 11 has greatly improved the Snipping Tool, including adding shapes to the image editor and the ability to take video as well. It is a great built-in option if you don’t want to install a third-party app.&lt;/li&gt;
&lt;li&gt;On Windows 10 I would recommend &lt;a href="https://getgreenshot.org" rel="noopener noreferrer"&gt;Greenshot&lt;/a&gt; if you don’t want all of the advanced features of ShareX.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Ctrl + Shift + Win + B: Restart the graphics driver. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is useful if your screen is not drawing properly or one of your monitors stops displaying.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Applications that support multiple tabs (e.g. web browsers, text editors, File Explorer on Windows 11+) often support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Ctrl + Tab to cycle between tabs in the application.&lt;/li&gt;
&lt;li&gt;
Ctrl + Shift + Tab to cycle between tabs in reverse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The functionality may vary depending on the application though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taskbar shortcuts
&lt;/h2&gt;

&lt;p&gt;The taskbar is the bar typically at the bottom of the screen that contains the Start button, pinned applications, open windows, and the system tray. There are a few shortcuts you can use when clicking on an application in the task bar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Shift + Left-click: Opens another instance of the application.&lt;/li&gt;
&lt;li&gt;
Ctrl + Shift + Left-click: Opens another instance of the application as an administrator.&lt;/li&gt;
&lt;li&gt;
Shift + Right-click: Show additional context menu options, such as Run As Admin or a Different User.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  File Explorer shortcuts
&lt;/h2&gt;

&lt;p&gt;When in the File Explorer, you can use the following shortcuts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Alt + Up: Go up one level in the folder hierarchy.&lt;/li&gt;
&lt;li&gt;
Alt + Left: Go back.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alt + Right: Go forward.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ctrl + N: Open a new File Explorer window at the current folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ctrl + T: Open a new tab (Windows 11+).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ctrl + Shift + N: Create a new folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alt + D: Focus the address bar so you can type in it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alt + Enter: Show properties for the selected file/folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;F2: Rename the selected file/folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shift + Delete: Permanently delete the selected items without moving them to the Recycle Bin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ctrl + A: Select all items in the current view.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hold Shift and click to select a range of items.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hold Ctrl and click to select multiple individual items.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Just start typing the name of a file or folder to quickly jump to and select it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of the above shortcuts also work in other applications, such as web browsers and text editors.&lt;/p&gt;

&lt;p&gt;In the File Explorer address bar (or the Run dialog window), you can type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cmd&lt;/code&gt; to open a command prompt in the current folder.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pwsh&lt;/code&gt; to open a PowerShell 7+ prompt in the current folder, if you have it installed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shell:startup&lt;/code&gt; to open the Startup folder of the current user. 

&lt;ul&gt;
&lt;li&gt;Placing a shortcut to an application in this folder will make it start automatically when you log in.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shell:sendto&lt;/code&gt; to open the SendTo folder of the current user. 

&lt;ul&gt;
&lt;li&gt;Placing a shortcut to an application in this folder will make it appear in the Send To context menu when you right-click a file or folder.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shell:common startup&lt;/code&gt; to open the Startup folder for all users.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shell:common sendto&lt;/code&gt; to open the SendTo folder for all users.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shell:desktop&lt;/code&gt; to open the Desktop folder.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shell:downloads&lt;/code&gt; to open the Downloads folder.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shell:appsfolder&lt;/code&gt; to see all installed applications, including hidden ones, and right-click to uninstall ones you don’t need.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://youtu.be/-ixXAB2Gc0M?si=1lJ7Bx7UJqAQ6U5O" rel="noopener noreferrer"&gt;my YouTube video on File Explorer shortcuts&lt;/a&gt; for more!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyeakpwge9uijlvwpa08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyeakpwge9uijlvwpa08.png" alt="File Explorer address bar" width="798" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Learning and using keyboard shortcuts can significantly improve your productivity. Try to incorporate a few of these shortcuts into your daily routine.&lt;/p&gt;

&lt;p&gt;As I discover more great shortcuts I'll update &lt;a href="https://blog.danskingdom.com/My-favourite-Windows-shortcuts/" rel="noopener noreferrer"&gt;the original canonical post&lt;/a&gt;, so be sure to check it for updates!&lt;/p&gt;

&lt;p&gt;Know of a great shortcut that I missed? Please let me know in the comments!&lt;/p&gt;

</description>
      <category>windows</category>
      <category>productivity</category>
      <category>shortcuts</category>
    </item>
    <item>
      <title>Create an Azure app registration secret with expiry longer than 24 months</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Fri, 01 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/create-an-azure-app-registration-secret-with-expiry-longer-than-24-months-3j15</link>
      <guid>https://forem.com/deadlydog/create-an-azure-app-registration-secret-with-expiry-longer-than-24-months-3j15</guid>
      <description>&lt;p&gt;The Azure portal allows you to create app registration secrets with a maximum expiry of 24 months. This follows the security best practice of forcing you to rotate secrets regularly to minimize the risk of exposure. However, there are scenarios where you might want a secret that lasts longer than this limit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: Using long-lived secrets can increase the risk of exposure, so it’s typically not recommended for production applications. Especially for secrets that would grant access to sensitive resources or data, or that have high privileges. A better approach is to use short-lived secrets and automate rotating them regularly. Even better, use &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview" rel="noopener noreferrer"&gt;Federated Identity Credentials&lt;/a&gt; and avoid secrets and expirations altogether.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To create an Azure app registration (i.e. service principal) secret with an expiry longer than 2 years, you can use &lt;a href="https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest" rel="noopener noreferrer"&gt;the Azure CLI&lt;/a&gt;. Here’s an example command that creates a secret with a 5 year expiry, where &lt;code&gt;&amp;lt;application-id&amp;gt;&lt;/code&gt; is the Application (Client) ID of your app registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az ad app credential reset --id &amp;lt;application-id&amp;gt; --years 5

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new secret on the app registration and output the &lt;code&gt;password&lt;/code&gt;, which is the secret value. Be sure to copy the value and store it somewhere secure, such as an Azure Key Vault, as you will not be able to retrieve it again.&lt;/p&gt;

&lt;p&gt;You can do the same operation in PowerShell using the &lt;code&gt;Az&lt;/code&gt; PowerShell module (specifically the &lt;code&gt;Az.Resources&lt;/code&gt; module) and &lt;a href="https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azadappcredential" rel="noopener noreferrer"&gt;the New-AzADAppCredential cmdlet&lt;/a&gt;, but you must use the app registration’s Object ID instead of the Application ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$startDate = (Get-Date)
$endDate = $startDate.AddYears(5)
New-AzADAppCredential -ObjectId &amp;lt;object-id&amp;gt; -StartDate $startDate -EndDate $endDate -DisplayName 'User friendly description'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully you (and my future self) find this helpful.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqctzpbrzl6ahdr60lvkj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqctzpbrzl6ahdr60lvkj.png" alt="Azure secrets 2+ years" width="256" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
    </item>
    <item>
      <title>My favourite git aliases</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Wed, 02 Apr 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/my-favourite-git-aliases-255g</link>
      <guid>https://forem.com/deadlydog/my-favourite-git-aliases-255g</guid>
      <description>&lt;p&gt;Git is super powerful, but can also be confusing, especially when using the command line interface (CLI). It has so many commands, and I only regularly use a handful of them.&lt;/p&gt;

&lt;p&gt;To make it both easier to remember commands I rarely use, and to reduce the number of keystrokes needed to execute the ones I use all the time, I use Git aliases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the code!
&lt;/h2&gt;

&lt;p&gt;I go over each alias in detail below, but here’s the alias section taken directly from my current global &lt;code&gt;.gitconfig&lt;/code&gt; file in my user directory. e.g. &lt;code&gt;C:\Users\[Your Name]\.gitconfig&lt;/code&gt; on Windows, or &lt;code&gt;~/.gitconfig&lt;/code&gt; on Linux/Mac.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[alias]
  alias = !echo 'git config --get-regexp ^alias\\.' &amp;amp;&amp;amp; git config --get-regexp ^alias\\.
  ac = !echo 'git add -A , git commit -m' &amp;amp;&amp;amp; git add -A &amp;amp;&amp;amp; git commit -m
  b = !echo 'git branch' &amp;amp;&amp;amp; git branch
  browse = !echo 'start `git config get remote.origin.url`' &amp;amp;&amp;amp; start `git config get remote.origin.url`
  co = !echo 'git checkout' &amp;amp;&amp;amp; git checkout
  commit-empty = !echo 'git commit --allow-empty -m \"chore: Empty commit to re-trigger build\"' &amp;amp;&amp;amp; git commit --allow-empty -m \"chore: Empty commit to re-trigger build\"
  delete-local-branches-already-merged-in-remote = !echo 'git branch --merged | egrep -i -v(main|master|develop|dev|staging|release)| xargs -r git branch -d' &amp;amp;&amp;amp; git branch --merged | egrep -i -v '(main|master|develop|dev|staging|release)' | xargs -r git branch -d
  delete-local-branches-already-merged-in-remote-what-if = !echo 'git branch --merged | egrep -i -v(main|master|develop|dev|staging|release)//| xargs -r git branch -d' &amp;amp;&amp;amp; git branch --merged | egrep -i -v '(main|master|develop|dev|staging|release)'
  delete-local-tags = !echo 'git tag -l | xargs git tag -d &amp;amp;&amp;amp; git fetch --tags' &amp;amp;&amp;amp; git tag -l | xargs git tag -d &amp;amp;&amp;amp; git fetch --tags
  delete-stale-remote-tracking-branches-from-local = !echo 'git remote prune origin' &amp;amp;&amp;amp; git remote prune origin
  ge = !echo 'GitExtensions .' &amp;amp;&amp;amp; GitExtensions .
  gec = !echo 'GitExtensions commit' &amp;amp;&amp;amp; GitExtensions commit
  gtfo = !echo 'git reset --hard , git clean -xfd' &amp;amp;&amp;amp; git reset --hard &amp;amp;&amp;amp; git clean -xfd
  history = !echo 'git log --oneline --graph --decorate --all' &amp;amp;&amp;amp; git log --oneline --graph --decorate --all
  s = !echo 'git status' &amp;amp;&amp;amp; git status
  pushf = !echo 'git push --force-with-lease' &amp;amp;&amp;amp; git push --force-with-lease
  pushnew = !echo 'git push --set-upstream origin branch_name' &amp;amp;&amp;amp; git push --set-upstream origin `git symbolic-ref --short HEAD`
  stashall = !echo 'git stash push --include-untracked' &amp;amp;&amp;amp; git stash push --include-untracked

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will notice that each alias begins with &lt;code&gt;!echo '...' &amp;amp;&amp;amp;&lt;/code&gt;. This is not mandatory and you can remove it if you like; it will simply print the command to the console before executing it, so that you can see the actual commands being run.&lt;/p&gt;

&lt;p&gt;For brevity, I will not include the &lt;code&gt;!echo '...' &amp;amp;&amp;amp;&lt;/code&gt; part in the explanations below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating your .gitconfig file
&lt;/h2&gt;

&lt;p&gt;To add these aliases to your global &lt;code&gt;.gitconfig&lt;/code&gt; file, you can either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Use the &lt;code&gt;git config&lt;/code&gt; command to add them one by one. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For example, to add the &lt;code&gt;ac&lt;/code&gt; alias, you can run the following command in your terminal:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To mass edit, open the file in a text editor and add them manually. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To open the file in the default editor, use the following command:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Individual aliases in detail
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias = git config --get-regexp ^alias\\.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git alias&lt;/code&gt; will show you all the aliases you have set up in your &lt;code&gt;.gitconfig&lt;/code&gt; file.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ac = git add -A &amp;amp;&amp;amp; git commit -m

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git ac "The commit message"&lt;/code&gt; will add all changes to the staging area and commit them with the provided message.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;b = git branch

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git b&lt;/code&gt; will show you all the branches in your local repository. This alias just saves a few keystrokes.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;browse = start `git config get remote.origin.url`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git browse&lt;/code&gt; will open the remote repository in your default web browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use this to open the repository in GitHub, Azure DevOps, Bitbucket, etc. to create pull requests after committing changes to a branch and pushing them up, if you prefer the PR creation web experience to the IDE experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;start&lt;/code&gt; is a Windows command to open a file or URL in the default application. You may need to adjust this for the Linux/Mac equivalent, which is may be &lt;code&gt;open&lt;/code&gt; for Mac and &lt;code&gt;xdg-open&lt;/code&gt; for Linux.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;co = git checkout

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git co branch_name&lt;/code&gt; will switch to the specified branch. This alias just saves a few keystrokes.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit-empty = git commit --allow-empty -m \"chore: Empty commit to re-trigger build\"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git commit-empty&lt;/code&gt; will create an empty commit with the message &lt;code&gt;chore: Empty commit to re-trigger build&lt;/code&gt;. This is useful for re-triggering builds in CI/CD pipelines without making any code changes.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete-local-branches-already-merged-in-remote = git branch --merged | egrep -i -v '(main|master|develop|dev|staging|release)' | xargs -r git branch -d

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git delete-local-branches-already-merged-in-remote&lt;/code&gt; will delete all local branches that have already been merged into the remote repository, except for the specified branches (main, master, develop, dev, staging, release). This is useful for cleaning up your local branches after merging pull requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Change the branches in the &lt;code&gt;egrep&lt;/code&gt; command to match your own branch names that you never want it to delete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you don’t like the long verbose alias name, give it a new one! 😊&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete-local-branches-already-merged-in-remote-what-if = git branch --merged | egrep -i -v '(main|master|develop|dev|staging|release)'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git delete-local-branches-already-merged-in-remote-what-if&lt;/code&gt; will show you all local branches that would be deleted if you ran the &lt;code&gt;delete-local-branches-already-merged-in-remote&lt;/code&gt; alias command.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete-local-tags = git tag -l | xargs git tag -d &amp;amp;&amp;amp; git fetch --tags

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git delete-local-tags&lt;/code&gt; will delete all local tags and fetch the latest tags from the remote repository, ensuring that your local tags exactly match the remote’s.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delete-stale-remote-tracking-branches-from-local = git remote prune origin

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git delete-stale-remote-tracking-branches-from-local&lt;/code&gt; will delete any stale remote-tracking branches from your local repository. That is, if a branch has been deleted from the remote repository, this will remove it from your local repository as well if needed.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ge = GitExtensions .

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git ge&lt;/code&gt; will open the GitExtensions GUI for the current repository. This requires having &lt;a href="https://gitextensions.github.io/" rel="noopener noreferrer"&gt;GitExtensions&lt;/a&gt; installed on your machine.&lt;/p&gt;

&lt;p&gt;I use this when I’m not in an IDE, like VS Code, and want to view the history of branches and view their commits and diffs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you prefer a different git tool over GitExtensions, you may be able to replace &lt;code&gt;GitExtensions&lt;/code&gt; with the name of your preferred tool.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gec = GitExtensions commit

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git gec&lt;/code&gt; will open the GitExtensions GUI for committing changes in the current repository. Again, this requires having &lt;a href="https://gitextensions.github.io/" rel="noopener noreferrer"&gt;GitExtensions&lt;/a&gt; installed on your machine.&lt;/p&gt;

&lt;p&gt;I use this when I’m in the terminal and want a more rich and easy diff, stage, and commit experience than the command line provides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Again, if you prefer a different git tool over GitExtensions, you may be able to use it if it accepts command line arguments.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gtfo = git reset --hard &amp;amp;&amp;amp; git clean -xfd

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git gtfo&lt;/code&gt; will reset your current branch to the last commit, deleting any uncommitted changes and removing all untracked files and directories.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;history = git log --oneline --graph --decorate --all

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git history&lt;/code&gt; will show you a more compact representation of the commit history for all branches in your repository.&lt;/p&gt;

&lt;p&gt;Here is a sample output of the &lt;code&gt;git history&lt;/code&gt; alias:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn844daondv6o0uae58id.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn844daondv6o0uae58id.png" alt="Git history alias output" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compare that to the default &lt;code&gt;git log&lt;/code&gt; output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzywjkfyoh3o615vne7im.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzywjkfyoh3o615vne7im.png" alt="Git log output" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s = git status

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git s&lt;/code&gt; will show you the status of your current branch, including any uncommitted changes and the current branch name. This alias just saves a few keystrokes.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pushf = git push --force-with-lease

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git pushf&lt;/code&gt; will force push your changes to the remote repository, with a lease. This is useful when you need to overwrite the remote branch with your local changes, but you want to ensure that you don’t accidentally overwrite someone else’s changes by forgetting to add &lt;code&gt;--force-with-lease&lt;/code&gt;.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pushnew = git push --set-upstream origin `git symbolic-ref --short HEAD`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git pushnew&lt;/code&gt; will push your current branch to the remote repository and set the upstream branch to the same name. This is useful when you create a new branch locally and want to push it to the remote repository for the first time, and aren’t using an IDE that does this for you. This is similar to the &lt;code&gt;git push --set-upstream origin branch_name&lt;/code&gt; command, but it automatically uses the name of the current branch.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stashall = git stash push --include-untracked

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing &lt;code&gt;git stashall&lt;/code&gt; will stash all changes in your working directory, including untracked files. This is useful when you want to temporarily save your changes without committing them, and you want to include untracked files as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why aren’t you using PowerShell commands in the alias?
&lt;/h2&gt;

&lt;p&gt;I’m a huge proponent of PowerShell, so you may be wondering why I’m using unix commands like &lt;code&gt;xargs&lt;/code&gt; and &lt;code&gt;egrep&lt;/code&gt; in my aliases. The main reason is that PowerShell is not installed by default on Linux and Mac. Git Bash is installed with Git though, so I believe the aliases should work on all platforms and in non-PowerShell shells.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I use these aliases to make my life easier when working with Git on the command line. Hopefully you’ve found some of them useful, or they’ve inspired you to create new ones not listed here. Feel free to customize these aliases to fit your preferences.&lt;/p&gt;

&lt;p&gt;If you have suggestions for other aliases, please leave a comment below! If you found this post helpful, consider sharing it with friends and colleagues.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>git</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Reclaim disk space by cleaning all your Git repos with PowerShell</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Mon, 17 Feb 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/reclaim-disk-space-by-cleaning-all-your-git-repos-with-powershell-4cjn</link>
      <guid>https://forem.com/deadlydog/reclaim-disk-space-by-cleaning-all-your-git-repos-with-powershell-4cjn</guid>
      <description>&lt;p&gt;Developers often have tens or hundreds of Git repositories on their machines. When we are done with a project, we often forget to clean up the repository, leaving temporary files like build artifacts, logs, packages, modules, and other files taking up valuable disk space.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://git-scm.com/docs/git-clean" rel="noopener noreferrer"&gt;git clean command&lt;/a&gt; can be used to remove untracked files and free up disk space. However, running &lt;code&gt;git clean -xfd&lt;/code&gt; in each repository manually can be a hassle. Luckily, we can automate this process using PowerShell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean all repos using the GitClean PowerShell module
&lt;/h2&gt;

&lt;p&gt;To make it easy to clean all your Git repositories, I created the cross-platform &lt;a href="https://github.com/deadlydog/PowerShell.GitClean" rel="noopener noreferrer"&gt;GitClean PowerShell module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Assuming all of your Git repositories are located under a common root folder, &lt;code&gt;C:\dev\Git&lt;/code&gt; for example, you can install the module and clean all your repos with the following PowerShell code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Module -Name GitClean
Invoke-GitClean -RootDirectoryPath 'C:\dev\Git'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wym1vyzcyit2st6ljsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wym1vyzcyit2st6ljsk.png" alt="Screenshot of running the Invoke-GitClean command" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screenshot above you can see it found 262 Git repositories, cleaned 260 of them, skipped cleaning 2 of them because they had untracked files that would have been lost, and freed up 33.4 GB of disk space. Easy 😊&lt;/p&gt;

&lt;p&gt;No more node_modules of old projects taking up all my disk space 😅&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mkf83hicfy8235hsjnr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mkf83hicfy8235hsjnr.jpeg" alt="meme of node_modules directory being huge" width="669" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative PowerShell one-liner
&lt;/h2&gt;

&lt;p&gt;Not everyone likes installing modules on their system, so as an alternative, here is a simple PowerShell script you can run to clean all your Git repositories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[string] $rootDirectoryPath = 'C:\dev\Git'

# Find all '.git' directories below the given path.
Get-ChildItem -Path $rootDirectoryPath -Recurse -Depth 3 -Force -Directory -Filter '.git' |
    # Get the directory path of the Git repository.
    ForEach-Object {
        $gitDirectoryPath = $_.FullName
        $gitRepositoryDirectoryPath = Split-Path -Path $gitDirectoryPath -Parent
        Write-Output $gitRepositoryDirectoryPath
    } |
    # Only include Git repositories that do not have untracked files.
    Where-Object {
        $gitOutput = Invoke-Expression "git -C ""$_"" status" | Out-String
        [bool] $hasUntrackedFiles = $gitOutput.Contains('Untracked files')
        if ($hasUntrackedFiles) {
            Write-Warning "Skipping Git repository with untracked files: $_"
        }
        Write-Output (-not $hasUntrackedFiles)
    } |
    # Clean the Git repository.
    ForEach-Object {
        Write-Output "Cleaning Git repository: $_"
        Invoke-Expression "git -C ""$_"" clean -xdf"
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can &lt;a href="///assets/Posts/2025-02-17-Reclaim-disk-space-by-cleaning-all-your-Git-repos-with-PowerShell/CleanGitRepositories.ps1"&gt;download this snippet as a script here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This won’t give you the nice progress bars and detailed information that the GitClean module does, but it will get the job done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It’s easy to forget to clean up old Git repositories. When you do remember to, the GitClean module can make it quick and painless. You might even want to create a scheduled task or cron job to run it automatically every month to keep your disk space in check.&lt;/p&gt;

&lt;p&gt;I hope you find this module useful. Happy coding! 😊&lt;/p&gt;

</description>
      <category>git</category>
      <category>powershell</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Windows settings I change after a clean install</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Sat, 08 Feb 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/deadlydog/windows-settings-i-change-after-a-clean-install-2knb</link>
      <guid>https://forem.com/deadlydog/windows-settings-i-change-after-a-clean-install-2knb</guid>
      <description>&lt;p&gt;Windows has a ton of native settings and features that can make it more pleasant and efficient to use. Below are some of my favorites that I always adjust after a fresh install of Windows. These are all personal preference, and I show the settings I like, but use whatever works best for you.&lt;/p&gt;

&lt;p&gt;This post was written with Windows 11 in mind, so some of the settings may not be available in other versions of Windows, or you may need to access them differently.&lt;/p&gt;

&lt;p&gt;As I discover more awesome settings I’ll add them &lt;a href="https://blog.danskingdom.com/Windows-settings-I-change-after-a-clean-install/" rel="noopener noreferrer"&gt;to the canonical post here&lt;/a&gt;, so be sure to check there from time to time. Also, if you have a favourite that I don’t have listed here, please let me know in the comments below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you enjoy this post, check out my &lt;a href="https://blog.danskingdom.com/My-favourite-Windows-shortcuts/" rel="noopener noreferrer"&gt;My favourite Windows shortcuts&lt;/a&gt; post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  File Explorer settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Have File Explorer open to the This PC view
&lt;/h3&gt;

&lt;p&gt;By default, File Explorer opens to the &lt;code&gt;Home&lt;/code&gt; view, which shows a bunch of things, such as your most used folders and files. I find that view very cluttered, and often just want to get into the root of one of my drives, so I prefer to start at the &lt;code&gt;This PC&lt;/code&gt; view.&lt;/p&gt;

&lt;p&gt;Below are screenshots of the default &lt;code&gt;Home&lt;/code&gt; view and the &lt;code&gt;This PC&lt;/code&gt; view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro8fg3i17gjuzuhn4pg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro8fg3i17gjuzuhn4pg9.png" alt="File Explorer Home view" width="782" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudpqapwr5ikd6zpyfwu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudpqapwr5ikd6zpyfwu6.png" alt="File Explorer This PC view" width="727" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To change the default view of File Explorer to &lt;code&gt;This PC&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Start Menu, search for &lt;code&gt;File Explorer Options&lt;/code&gt; and open it.&lt;/li&gt;
&lt;li&gt;In the File Explorer Options window, change the &lt;code&gt;Open File Explorer to&lt;/code&gt; setting to &lt;code&gt;This PC&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl66cbbeu1wrjhleg4h4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl66cbbeu1wrjhleg4h4.png" alt="File Explorer Options window to change the default view to This PC" width="384" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Show hidden files and folders and file extensions in File Explorer
&lt;/h3&gt;

&lt;p&gt;By default, File Explorer will not show hidden files and folders, nor show file extensions for known file types. I don’t like Windows hiding things from me, so I always enable these settings.&lt;/p&gt;

&lt;p&gt;Below are screenshots showing how a directory in File Explorer might look before and after making the changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9h84oslvzbhnp2c92r7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9h84oslvzbhnp2c92r7.png" alt="Before showing hidden files and file extensions" width="342" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62ka6q288hfnwr7w9epy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62ka6q288hfnwr7w9epy.png" alt="After showing hidden files and file extensions" width="358" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable the settings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open File Explorer Options. 

&lt;ul&gt;
&lt;li&gt;You can search for &lt;code&gt;File Explorer Options&lt;/code&gt; in the Start Menu, or find it in File Explorer by clicking the &lt;code&gt;View&lt;/code&gt; tab, then &lt;code&gt;Options&lt;/code&gt; on the right side (Windows 10), or the &lt;code&gt;...&lt;/code&gt; button then &lt;code&gt;Options&lt;/code&gt; (Windows 11).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In the Folder Options window, click on the &lt;code&gt;View&lt;/code&gt; tab.&lt;/li&gt;
&lt;li&gt;Under &lt;code&gt;Advanced settings&lt;/code&gt;, find the following settings: 

&lt;ul&gt;
&lt;li&gt;Enable &lt;code&gt;Show hidden files, folders, and drives&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Disable &lt;code&gt;Hide extensions for known file types&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Apply&lt;/code&gt; to apply it to the current folder.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Apply to Folders&lt;/code&gt; to apply it to all folders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqr1bnr6gdvb65dv83d59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqr1bnr6gdvb65dv83d59.png" alt="Screenshot showing how to enable showing hidden files and file extensions" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step of clicking the &lt;code&gt;Apply to Folders&lt;/code&gt; button is important so that you don’t have to do this for every folder you open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mouse settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Show where the mouse cursor is with the Ctrl key
&lt;/h3&gt;

&lt;p&gt;If you have multiple monitors, it’s easy to lose the mouse cursor. Windows has a feature that when you press the Ctrl key, it will show you where the mouse cursor is by briefly circling it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9darrm26n5yt4mj3dcg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9darrm26n5yt4mj3dcg.gif" alt="Windows showing where the mouse cursor is" width="264" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable this feature:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the Start Menu to search for &lt;code&gt;Mouse settings&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the Mouse settings window, open the &lt;code&gt;Additional mouse settings&lt;/code&gt;, which should pop up the &lt;code&gt;Mouse Properties&lt;/code&gt; window.&lt;/li&gt;
&lt;li&gt;Click on the &lt;code&gt;Pointer Options&lt;/code&gt; tab.&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;Show location of pointer when I press the CTRL key&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b3ux30qvlhyftxdcd4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b3ux30qvlhyftxdcd4w.png" alt="Mouse properties window showing how to enable showing the mouse cursor location" width="402" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bonus: See the Microsoft PowerToys section at the end to allow spotlighting the mouse cursor when you press the Ctrl key twice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make the mouse cursor larger and change the color
&lt;/h3&gt;

&lt;p&gt;Having a large resolution on a small monitor can make it difficult to see the mouse cursor. I like to enlarge my cursor a bit, and change the color to make it stand out so it’s easier to see.&lt;/p&gt;

&lt;p&gt;To adjust your mouse cursor settings, use the Start Menu to search for &lt;code&gt;Mouse pointer size&lt;/code&gt;. There you can adjust the size and color of the mouse cursor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lzt6gigqm2qdq1kefjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lzt6gigqm2qdq1kefjv.png" alt="Windows mouse cursor settings" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable the Touchpad when using a mouse
&lt;/h3&gt;

&lt;p&gt;When using Windows on a laptop without an external keyboard, I sometimes accidentally touch the touchpad while typing, which causes the cursor to jump around. To prevent this, I disable the touchpad when a mouse is connected.&lt;/p&gt;

&lt;p&gt;To disable the touchpad when a mouse is connected:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the Start Menu to search for &lt;code&gt;Touchpad settings&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Expand the &lt;code&gt;Touchpad&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;Uncheck &lt;code&gt;Leave touchpad on when a mouse is connected&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Change Touchpad triple-tap to middle-click
&lt;/h3&gt;

&lt;p&gt;By default Windows opens the Windows search when you triple-tap the touchpad. That’s what the Windows key is for. Instead, I prefer to have the triple-tap act as a middle-click.&lt;/p&gt;

&lt;p&gt;To change the touchpad triple-tap to middle-click:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the touchpad settings window, expand the &lt;code&gt;Three-finger gestures&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;Taps&lt;/code&gt; dropdown to &lt;code&gt;Middle mouse button&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7te96meap4xk9gmbcud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7te96meap4xk9gmbcud.png" alt="Touchpad settings to disable the touchpad when a mouse is connected and set triple-tap to middle-click" width="651" height="913"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Taskbar settings
&lt;/h2&gt;

&lt;p&gt;The Windows taskbar has many settings that can be adjusted.&lt;/p&gt;

&lt;p&gt;To open the taskbar settings window:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the taskbar and choose &lt;code&gt;Taskbar settings&lt;/code&gt;, or you can search the Start Menu for &lt;code&gt;Taskbar settings&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vkkzfgt6n7qkpq8dfti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vkkzfgt6n7qkpq8dfti.png" alt="Taskbar settings I always adjust" width="573" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hide the search box from the taskbar
&lt;/h3&gt;

&lt;p&gt;When I want to search, I use the Win key and start typing, so I don’t like the search box taking up space on the taskbar.&lt;/p&gt;

&lt;p&gt;In the taskbar settings window, in the &lt;code&gt;Taskbar items&lt;/code&gt; section, change the &lt;code&gt;Search&lt;/code&gt; to &lt;code&gt;Hide&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hide the task view button
&lt;/h3&gt;

&lt;p&gt;I don’t use multiple task views, and use Alt + Tab to switch between windows, so I hide the task view button. You can also use Win + Tab to open the task view.&lt;/p&gt;

&lt;p&gt;In the taskbar settings window, in the &lt;code&gt;Taskbar items&lt;/code&gt; section, toggle the &lt;code&gt;Task view&lt;/code&gt; to &lt;code&gt;Off&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable widgets
&lt;/h3&gt;

&lt;p&gt;I don’t use the widgets on the taskbar and find them distracting, so I disable them.&lt;/p&gt;

&lt;p&gt;In the taskbar settings window, in the &lt;code&gt;Taskbar items&lt;/code&gt; section, toggle the &lt;code&gt;Widgets&lt;/code&gt; to &lt;code&gt;Off&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzflh9b056jca06oh2xxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzflh9b056jca06oh2xxf.png" alt="Screenshot showing the widgets, search box, and task view button on the taskbar" width="628" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Decide which system tray icons to always show
&lt;/h3&gt;

&lt;p&gt;The system tray is the area on the right side of the taskbar where you can see the time, volume, network status, and other system icons. There are some tray icons that I always want to see, and others that I don’t care about. You can control which ones are always shown, and which ones are hidden behind the &lt;code&gt;Show hidden icons&lt;/code&gt; button to save space on the taskbar.&lt;/p&gt;

&lt;p&gt;In the taskbar settings window, expand the &lt;code&gt;Other system tray icons&lt;/code&gt; section, then toggle &lt;code&gt;On&lt;/code&gt; the apps that you want to always show, and &lt;code&gt;Off&lt;/code&gt; the ones to hide.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d1agtj5r5whylkb9lsr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d1agtj5r5whylkb9lsr.png" alt="System tray icons" width="487" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also simply drag-and-drop the system tray icons to/from the hidden area to control which ones are always shown.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shift the Start Menu to the left
&lt;/h3&gt;

&lt;p&gt;Windows 11 moved the Start Menu to the center of the taskbar by default. I prefer the previous Windows behaviour where it was on the left.&lt;/p&gt;

&lt;p&gt;In the taskbar settings window, in the &lt;code&gt;Taskbar behaviors&lt;/code&gt; section, change the &lt;code&gt;Taskbar alignment&lt;/code&gt; to &lt;code&gt;Left&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Never group taskbar buttons
&lt;/h3&gt;

&lt;p&gt;I like to see all of my taskbar buttons including their labels, so I disable grouping them so it’s easy to see which ones are open and quickly select the one I want.&lt;/p&gt;

&lt;p&gt;Here is a screenshot of the taskbar with the buttons combined:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtpf8bg3ffit9rd1yuu1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtpf8bg3ffit9rd1yuu1.png" alt="Taskbar with combined taskbar icons" width="506" height="50"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the same taskbar with the buttons not combined:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwsbe206tmgrcocjz4eo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwsbe206tmgrcocjz4eo.png" alt="Taskbar with taskbar icons never combined" width="800" height="30"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the taskbar settings window, in the &lt;code&gt;Taskbar behaviors&lt;/code&gt; section, change the &lt;code&gt;Combine taskbar buttons and hide labels&lt;/code&gt; to &lt;code&gt;Never&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3jv7bkpz7fcqcljwy3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3jv7bkpz7fcqcljwy3t.png" alt="Taskbar settings to never group taskbar icons" width="665" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable End Task on right-clicking taskbar icons
&lt;/h3&gt;

&lt;p&gt;Typically when an application hangs, you need to open the Task Manager to kill the app. You can enable &lt;code&gt;End Task&lt;/code&gt; in the right-click context menu of taskbar icons to make it easier to kill unresponsive apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvejudmy1xounykroopk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvejudmy1xounykroopk.png" alt="Screenshot of right-clicking a taskbar icon to show the End Task option" width="290" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable &lt;code&gt;End Task&lt;/code&gt; on right-clicking taskbar icons, go to &lt;code&gt;Settings&lt;/code&gt; &amp;gt; &lt;code&gt;System&lt;/code&gt; &amp;gt; &lt;code&gt;For developers&lt;/code&gt; and enable &lt;code&gt;End Task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mv9xk5lv0d1c53z2tdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mv9xk5lv0d1c53z2tdc.png" alt="Enable End Task on right-clicking taskbar icons" width="660" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Desktop settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Change your desktop background image
&lt;/h3&gt;

&lt;p&gt;By default Windows will often display a different desktop background image each day from Bing. I actually quite like this functionality for my personal computer, but prefer to have a static image on my work computer where I often do screen sharing. You may also want to adjust the desktop image if you remote desktop to other computers regularly, so that it’s easy to tell when you are working on your local computer or a remote one.&lt;/p&gt;

&lt;p&gt;To change the desktop background image:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the desktop and choose &lt;code&gt;Personalize&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the Personalization settings window, click on &lt;code&gt;Background&lt;/code&gt;. 

&lt;ol&gt;
&lt;li&gt;You could also get here by searching the Start Menu for &lt;code&gt;Background image settings&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;In the &lt;code&gt;Personalize your background&lt;/code&gt; section, choose &lt;code&gt;Picture&lt;/code&gt; from the dropdown. 

&lt;ol&gt;
&lt;li&gt;If you want to cycle through multiple images, you can choose &lt;code&gt;Slideshow&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Click the &lt;code&gt;Browse&lt;/code&gt; button to select a picture or directory from your computer.&lt;/li&gt;

&lt;li&gt;You may want to change the &lt;code&gt;Choose a fit&lt;/code&gt; setting to &lt;code&gt;Fill&lt;/code&gt;, &lt;code&gt;Fit&lt;/code&gt;, or &lt;code&gt;Stretch&lt;/code&gt; to make the image look better on your screen.&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd2rcb2xe05m1yzzaefq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd2rcb2xe05m1yzzaefq.png" alt="Windows background image settings" width="654" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn transparency effects off
&lt;/h3&gt;

&lt;p&gt;I don’t like transparency in my windows and menus, and you get a small performance boost by disabling it.&lt;/p&gt;

&lt;p&gt;To disable transparency effects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the desktop and choose &lt;code&gt;Personalize&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the Personalization settings window, click on &lt;code&gt;Colors&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Toggle the &lt;code&gt;Transparency effects&lt;/code&gt; switch to &lt;code&gt;Off&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29cnaqzqml5adgvg3esy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29cnaqzqml5adgvg3esy.png" alt="Windows transparency effects disabled" width="617" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Power settings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Don’t turn off the screen and go to sleep so fast
&lt;/h3&gt;

&lt;p&gt;Windows will often turn the monitor off or put the computer to sleep after a short period of inactivity, such as 5 or 15 minutes depending on if it is on battery or not. I like to extend this time so that if I walk away from my computer for a little while, it’s less likely to be asleep when I come back.&lt;/p&gt;

&lt;p&gt;To change the power settings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the Start Menu to search for &lt;code&gt;Power settings&lt;/code&gt; and open the &lt;code&gt;Power, sleep, and battery settings&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Expand the &lt;code&gt;Screen and sleep&lt;/code&gt; section if needed.&lt;/li&gt;
&lt;li&gt;Adjust when the computer turns off the screen and goes to sleep to your liking.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Turn on enhanced performance power mode
&lt;/h3&gt;

&lt;p&gt;In that same &lt;code&gt;Power, sleep, and battery settings&lt;/code&gt; window, you can adjust the &lt;code&gt;Power mode&lt;/code&gt; to prioritize performance over battery life and energy efficiency if you like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenl0ozzhiv2xxl0i6186.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenl0ozzhiv2xxl0i6186.png" alt="Windows power settings" width="694" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn the computer off when I press the power button
&lt;/h3&gt;

&lt;p&gt;By default, Windows will put the computer to sleep when you close the lid, or press the power button. I prefer to have the computer actually shut down when I press the power button. You may want your laptop to do nothing when the lid is closed, so it keeps running.&lt;/p&gt;

&lt;p&gt;To change the power button and lid settings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the Start Menu to search for &lt;code&gt;Close lid&lt;/code&gt; and open the &lt;code&gt;Change what closing the lid does&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Adjust the &lt;code&gt;When I close the lid&lt;/code&gt; and &lt;code&gt;When I press the power button&lt;/code&gt; settings to your liking.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save changes&lt;/code&gt; to apply the settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxkulijmorf97gtaup4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxkulijmorf97gtaup4z.png" alt="Power button and lid settings" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Show multiple time zones in the taskbar clock
&lt;/h3&gt;

&lt;p&gt;You can configure multiple time zones to show in the taskbar clock when you hover over it or click on it. This is great if you work with people in different time zones, travel frequently, or are a software developer and just want to know what the current UTC time is 😄.&lt;/p&gt;

&lt;p&gt;To add additional time zones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the taskbar clock and choose &lt;code&gt;Adjust date/time&lt;/code&gt;. 

&lt;ol&gt;
&lt;li&gt;You can also search the Start Menu for &lt;code&gt;Date &amp;amp; time settings&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;In the &lt;code&gt;Date &amp;amp; time&lt;/code&gt; settings window, scroll down and click on the &lt;code&gt;Additional clocks&lt;/code&gt; section.&lt;/li&gt;

&lt;li&gt;The &lt;code&gt;Date and Time&lt;/code&gt; window should open, allowing you to add up to 2 additional clocks and give them friendly names.&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27z98qkn102cqg8a2l7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F27z98qkn102cqg8a2l7c.png" alt="Date and Time settings with additional clocks" width="451" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result when hovering the mouse over the clock:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2q044ffgjdmpty86xc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2q044ffgjdmpty86xc7.png" alt="Hovering over the clock to show additional time zones" width="162" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And when clicking on the clock:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F292l4extqank6y5hvfqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F292l4extqank6y5hvfqp.png" alt="Clicking on the clock to show additional time zones" width="355" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the Windows Sandbox to test software and settings safely
&lt;/h3&gt;

&lt;p&gt;Windows includes an app called &lt;code&gt;Windows Sandbox&lt;/code&gt;. When you open the Windows Sandbox app, it creates a clean virtual environment that looks like a fresh install of Windows. Here you can experiment with software, settings, or files without affecting your main system, and when you close the app everything is erased. This is great for testing software or settings that you’re not sure about, without risking your main system. Just be sure not to save anything important in the Windows Sandbox, as it will be erased when you close it.&lt;/p&gt;

&lt;p&gt;The Windows Sandbox app is not installed by default, so you may need to install it from the Windows Features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search the Start Menu for &lt;code&gt;Windows Features&lt;/code&gt;, and choose &lt;code&gt;Turn Windows features on or off&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the Windows Features window, scroll down and enable &lt;code&gt;Windows Sandbox&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;OK&lt;/code&gt; to install it.&lt;/li&gt;
&lt;li&gt;Restart your computer if prompted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2h2gjavrz236gybkejfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2h2gjavrz236gybkejfz.png" alt="The Windows Features window showing Windows Sandbox enabled" width="416" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now search for &lt;code&gt;Windows Sandbox&lt;/code&gt; in the Start Menu to open the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5g3hfpuy6zh77va4t0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5g3hfpuy6zh77va4t0p.png" alt="Windows Sandbox app" width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Registry tweaks that don’t have a GUI setting
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: Modifying the registry can be dangerous if you don’t know what you’re doing, so be sure to &lt;a href="https://www.tweaking.com/how-to-backup-whole-registry-in-windows-10-step-by-step-guide/" rel="noopener noreferrer"&gt;backup your registry&lt;/a&gt; before making any manual changes.&lt;/p&gt;

&lt;p&gt;You can try the commands below in the Windows Sandbox first to see if you like the changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I provide terminal commands instead of manual point-and-click steps for modifying the registry to help ensure that only the intended keys are modified. You can run these commands in PowerShell or Command Prompt.&lt;/p&gt;

&lt;p&gt;Some commands shown below may need to be ran from an elevated terminal (e.g. run PowerShell as an Administrator), otherwise you may get an error like &lt;code&gt;Requested registry access is not allowed&lt;/code&gt;. Also, you may need to restart your computer for some changes to take effect.&lt;/p&gt;

&lt;p&gt;If you are curious about the syntax of the registry commands, you can find &lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/reg" rel="noopener noreferrer"&gt;the reg.exe MS docs here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to easily apply all of the registry tweaks below, simply &lt;a href="///assets/Posts/2025-02-08-Windows-settings-I-change-after-a-clean-install/All_registry_changes.reg"&gt;download this .reg file&lt;/a&gt; and double-click it to run it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable searching the web in the Start Menu search
&lt;/h3&gt;

&lt;p&gt;When I search from the Start Menu, I want it to only search my local machine.&lt;/p&gt;

&lt;p&gt;Here are screenshots before and after disabling the web search:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzolui1n63a5njke5prm3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzolui1n63a5njke5prm3.png" alt="Before disabling web search" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2For5yvgqoii23ov3n5kwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2For5yvgqoii23ov3n5kwa.png" alt="After disabling web search" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to disable web search in the Start Menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" /v BingSearchEnabled /t REG_DWORD /d 0 /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To revert back to the default behavior, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe delete "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" /v BingSearchEnabled /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that it also disables Copilot, which may or may not be desired.&lt;/p&gt;

&lt;h3&gt;
  
  
  Always open “More options” context menu in Windows 11+
&lt;/h3&gt;

&lt;p&gt;Windows 11 modified the right-click context menu to only show a few options by default, and you have to click “Show more options” to see the rest. I prefer the previous Windows behavior where all options are shown by default.&lt;/p&gt;

&lt;p&gt;Below are screenshots of the context menu before and after running the command.&lt;/p&gt;

&lt;p&gt;Default right-click context menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7cb6f952lyivupws2qh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7cb6f952lyivupws2qh.png" alt="Default right-click context menu" width="296" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right-click context menu with all options shown:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fv9phgbsmhyzcqi14eb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fv9phgbsmhyzcqi14eb.png" alt="Right-click context menu with all options shown" width="326" height="839"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to always show all options in the context menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To revert back to the default behavior, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.elevenforum.com/t/disable-show-more-options-context-menu-in-windows-11.1589/" rel="noopener noreferrer"&gt;Source and more info&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: Windows 11 updated to allow pressing Shift + Right-click to show all options in the context menu, so you may prefer to do that over using this registry tweak.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Speed up the right-click context menu
&lt;/h3&gt;

&lt;p&gt;When you right-click a file or folder, Windows has an artificial delay of 400 milliseconds before showing the context menu.&lt;/p&gt;

&lt;p&gt;Run the following command to change the delay to 50 milliseconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe add "HKCU\Control Panel\Desktop" /v MenuShowDelay /t REG_SZ /d 50 /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To revert back to the default behavior, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe add "HKCU\Control Panel\Desktop" /v MenuShowDelay /t REG_SZ /d 400 /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, before the context menu is shown, the &lt;code&gt;Send to&lt;/code&gt; menu discovers all of the apps and connected devices you can send the file to before it actually shows the menu. We can change this so that it does not enumerate devices until you hover over the &lt;code&gt;Send to&lt;/code&gt; menu.&lt;/p&gt;

&lt;p&gt;Run the following command to delay Send To from looking for devices right away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" /v DelaySendToMenuBuild /t REG_DWORD /d 1 /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To revert back to the default behavior, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" /v DelaySendToMenuBuild /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.makeuseof.com/windows-11-menus-felt-sluggish-until-i-tweaked-this-one-registry/" rel="noopener noreferrer"&gt;Source&lt;/a&gt; and &lt;a href="https://www.winhelponline.com/blog/hidden-registry-settings-sendto-menu-windows-7/" rel="noopener noreferrer"&gt;more info&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable delay of startup apps
&lt;/h3&gt;

&lt;p&gt;Windows puts a delay of 10 seconds before it starts launching apps in the Startup folder.&lt;/p&gt;

&lt;p&gt;Run the following command to disable the startup apps delay:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Serialize" /v Startupdelayinmsec /t REG_DWORD /d 0 /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To revert back to the default behavior, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reg.exe delete "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Serialize" /v Startupdelayinmsec /f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.elevenforum.com/t/enable-or-disable-delay-of-running-startup-apps-in-windows-11.9144/" rel="noopener noreferrer"&gt;Source and more info&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Microsoft PowerToys
&lt;/h2&gt;

&lt;p&gt;I was planning to keep this post to only native Windows features and settings, but I couldn’t resist including Microsoft PowerToys. PowerToys is kind of Microsoft’s way of quickly experimenting and iterating on features that &lt;em&gt;should&lt;/em&gt; be in Windows, but aren’t. If a feature gets popular enough, it may be included in Windows natively.&lt;/p&gt;

&lt;p&gt;Microsoft PowerToys is an application that adds a ton of great features to Windows, and it is being updated all the time.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://learn.microsoft.com/en-us/windows/powertoys/" rel="noopener noreferrer"&gt;Microsoft PowerToys docs&lt;/a&gt; for extensive documentation on all of the features, and how to install and configure them.&lt;/p&gt;

&lt;p&gt;A few of my favourite features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always On Top: Keep a window always on top of others.&lt;/li&gt;
&lt;li&gt;Find My Mouse: Spotlight the mouse cursor when you press the Ctrl key twice.&lt;/li&gt;
&lt;li&gt;File Locksmith: Easily unlock files that are in use by other processes.&lt;/li&gt;
&lt;li&gt;Image Resizer: Resize images quickly and easily.&lt;/li&gt;
&lt;li&gt;Color Picker: Get the RGB, HEX, and HSL values of any color on the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can install PowerToys by running the following command in a terminal, such as PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;winget install --id Microsoft.PowerToys --source winget

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you found some of these settings and features helpful. Have a favourite or one that I didn’t mention? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;Happy customizing!&lt;/p&gt;

</description>
      <category>performance</category>
      <category>productivity</category>
      <category>windows</category>
    </item>
  </channel>
</rss>
