<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>2026 on Funky Si's Blog (Dev)</title><link>https://blog-dev.funkysi1701.com/2026/</link><description>Recent content in 2026 on Funky Si's Blog (Dev)</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><managingEditor>funkysi1701@gmail.com (Simon Foster)</managingEditor><webMaster>funkysi1701@gmail.com (Simon Foster)</webMaster><lastBuildDate>Tue, 18 Aug 2026 08:00:00 +0000</lastBuildDate><atom:link href="https://blog-dev.funkysi1701.com/2026/index.xml" rel="self" type="application/rss+xml"/><item><title>GitOps with Flux on my k3s cluster</title><link>https://blog-dev.funkysi1701.com/posts/2026/gitops-with-flux-on-my-k3s-cluster/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Tue, 18 Aug 2026 08:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/gitops-with-flux-on-my-k3s-cluster/</guid><category term="Flux">Flux</category><category term="GitOps">GitOps</category><category term="Kubernetes">Kubernetes</category><category term="Helm">Helm</category><category term="k3s">k3s</category><category term="DevOps">DevOps</category><category term="SOPS">SOPS</category><category term="cert-manager">cert-manager</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/gitops-with-flux-k3s.png"/><description>&lt;p&gt;I already knew how to put things &lt;em&gt;on&lt;/em&gt; a Kubernetes cluster. &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/deploying-hugo-with-helm/"&gt;Helm&lt;/a&gt;
packages the YAML. &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/learning-kubernetes/"&gt;&lt;code&gt;kubectl apply&lt;/code&gt;&lt;/a&gt;
makes it exist. &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/kubernetes-and-letsencrypt/"&gt;cert-manager&lt;/a&gt;
mints the certificates. What I did not have was a boring answer to “what happens next week, when I have forgotten which laptop I ran that from?”&lt;/p&gt;
&lt;p&gt;GitOps is that answer, at least for &lt;strong&gt;platform&lt;/strong&gt; on my homelab. In practice, GitOps means keeping the desired cluster state in git and having an in-cluster controller continuously reconcile the live cluster back to that state. &lt;strong&gt;&lt;code&gt;simon-cluster&lt;/code&gt;&lt;/strong&gt; is the name I gave the &lt;a href="https://k3s.io/" target="_blank" rel="noopener noreferrer"&gt;k3s&lt;/a&gt;
cluster I run at home — a small Kubernetes estate for shared ingress, certificates, monitoring, and the GitHub Actions runners this blog builds on. &lt;a href="https://fluxcd.io/" target="_blank" rel="noopener noreferrer"&gt;Flux CD&lt;/a&gt;
on that cluster watches a private platform repo and keeps it in line with &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The useful part is not “I put YAML in git.” It is the &lt;strong&gt;split&lt;/strong&gt;. Flux owns MetalLB, Traefik, cert-manager, GitHub runners, the in-cluster registry, Cloudflare Tunnel, and monitoring. Application Helm releases in &lt;code&gt;develop&lt;/code&gt; / &lt;code&gt;main&lt;/code&gt; / &lt;code&gt;test&lt;/code&gt; still come from &lt;strong&gt;their own repos and pipelines&lt;/strong&gt;. Azure DevOps is still in that second column. Flux is not pretending to be the whole estate.&lt;/p&gt;
&lt;h2 id="pull-not-push"&gt;Pull, not push&lt;a class="anchor ms-1" href="#pull-not-push" aria-label="Permalink: Pull, not push"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A lot of “GitOps” talk is really CI with extra steps: a pipeline authenticates to the cluster and runs &lt;code&gt;helm upgrade&lt;/code&gt;. That is a &lt;strong&gt;push&lt;/strong&gt;. It works. I still do it for apps.&lt;/p&gt;
&lt;p&gt;Flux is a &lt;strong&gt;pull&lt;/strong&gt;. The cluster has a &lt;code&gt;GitRepository&lt;/code&gt; pointed at a private platform repo on &lt;code&gt;main&lt;/code&gt;. Child &lt;code&gt;Kustomization&lt;/code&gt; objects apply paths under that repo on a schedule. If someone (or some leftover pipeline) changes a Flux-managed object by hand, the next reconcile puts it back.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;source.toolkit.fluxcd.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;GitRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;flux-system&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;flux-system&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;interval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ref&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branch&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;url&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://github.com/example/example-config.git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bootstrap is once: install the Flux controllers (&lt;code&gt;gotk-components.yaml&lt;/code&gt;), give Flux a git credential and an age key for SOPS, apply &lt;code&gt;gotk-sync.yaml&lt;/code&gt;. After that I mostly work in git rather than reaching for kubeconfig.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add -A &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit -m &lt;span style="color:#e6db74"&gt;&amp;#34;…&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flux get all -A
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;flux reconcile kustomization flux-system --with-source
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I still keep &lt;code&gt;kubectl&lt;/code&gt; for poking at pods. I try not to use it as the source of truth for anything Flux already owns.&lt;/p&gt;
&lt;h2 id="how-the-repo-is-laid-out"&gt;How the repo is laid out&lt;a class="anchor ms-1" href="#how-the-repo-is-laid-out" aria-label="Permalink: How the repo is laid out"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clusters/simon-cluster/ # Flux bootstrap + child Kustomizations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;infrastructure/ # HelmRepositories, HelmReleases, CRs, SOPS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;clusters/simon-cluster&lt;/code&gt; is the entry point Flux syncs. Each child Kustomization is a named slice of the platform with its own interval, &lt;code&gt;dependsOn&lt;/code&gt;, and (where secrets live) SOPS decryption. &lt;code&gt;infrastructure/&lt;/code&gt; is the actual YAML those slices apply.&lt;/p&gt;
&lt;p&gt;Order matters. Helm charts cannot install until the &lt;code&gt;HelmRepository&lt;/code&gt; objects exist. cert-manager cannot come up until those sources do. A shortened version of the cert-manager slice looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kustomize.toolkit.fluxcd.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Kustomization&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;flux-system&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;interval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;dependsOn&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sources&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./infrastructure/cert-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;prune&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;wait&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;decryption&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;provider&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sops&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;secretRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sops-age&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;prune: true&lt;/code&gt; is the bit people skip and then regret. If you delete the manifest and Flux still has prune off, the live object sits there forever looking “fine.” &lt;code&gt;wait: true&lt;/code&gt; plus &lt;code&gt;dependsOn&lt;/code&gt; is the other half of that: cert-manager does not race Traefik because the Kustomization will not move on until the slice is healthy.&lt;/p&gt;
&lt;p&gt;The Helm install itself is a &lt;code&gt;HelmRelease&lt;/code&gt;, not a pipeline task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;helm.toolkit.fluxcd.io/v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;HelmRelease&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;interval&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;chart&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;chart&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;cert-manager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;v1.21.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;sourceRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;HelmRepository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;jetstack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;flux-system&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Same idea as &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/deploying-hugo-with-helm/"&gt;deploying with Helm&lt;/a&gt;
, except the cluster is the thing that runs &lt;code&gt;helm&lt;/code&gt;, not me.&lt;/p&gt;
&lt;h2 id="one-change-end-to-end"&gt;One change, end to end&lt;a class="anchor ms-1" href="#one-change-end-to-end" aria-label="Permalink: One change, end to end"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A typical platform change is dull, which is the point.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Edit YAML under &lt;code&gt;infrastructure/…&lt;/code&gt; (chart version, values, a &lt;code&gt;ClusterIssuer&lt;/code&gt;, a runner replica count).&lt;/li&gt;
&lt;li&gt;Open a PR, merge to &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wait for the Kustomization interval, or &lt;code&gt;flux reconcile&lt;/code&gt; if I am impatient.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flux get hr,ks -A&lt;/code&gt; until the slice is &lt;code&gt;Ready&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If it is not Ready, read the condition before assuming git and the cluster agree. That command is the homelab equivalent of a pipeline log.&lt;/p&gt;
&lt;p&gt;I already wrote about &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/kubernetes-and-letsencrypt/"&gt;Let’s Encrypt on this cluster&lt;/a&gt;
when cert-manager was a &lt;code&gt;kubectl apply&lt;/code&gt; of upstream YAML. That install now lives in git as the HelmRelease above plus &lt;code&gt;ClusterIssuer&lt;/code&gt; manifests. The DNS-01 token is not in those files in plaintext; it is a SOPS-encrypted Secret Flux decrypts on the cluster.&lt;/p&gt;
&lt;p&gt;Grafana followed the same path. I first ran it in &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/setting-up-grafana/"&gt;Docker Compose for .NET metrics&lt;/a&gt;
. The homelab copy now sits in the &lt;code&gt;monitoring&lt;/code&gt; namespace, owned by Flux, with dashboards as ConfigMaps in the same repo. Compose taught me the product; GitOps is how it stays installed when I am not watching.&lt;/p&gt;
&lt;p&gt;This blog is in the picture too, just not as a website on the cluster. Production is still Azure Static Web Apps. What Flux &lt;em&gt;does&lt;/em&gt; provision for this repo is a pool of &lt;a href="https://github.com/actions/actions-runner-controller" target="_blank" rel="noopener noreferrer"&gt;Actions Runner Controller&lt;/a&gt;
runners labelled &lt;code&gt;k8s&lt;/code&gt;, so GitHub Actions can build on the homelab instead of burning hosted minutes. The &lt;code&gt;RunnerDeployment&lt;/code&gt; is YAML in that private platform repo. The workflows stay in &lt;em&gt;this&lt;/em&gt; git repo. That is the split in miniature.&lt;/p&gt;
&lt;h2 id="secrets-in-git-not-in-chat"&gt;Secrets in git, not in chat&lt;a class="anchor ms-1" href="#secrets-in-git-not-in-chat" aria-label="Permalink: Secrets in git, not in chat"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Platform secrets are &lt;code&gt;*.enc.yaml&lt;/code&gt; files encrypted with &lt;a href="https://github.com/getsops/sops" target="_blank" rel="noopener noreferrer"&gt;SOPS&lt;/a&gt;
and &lt;a href="https://age-encryption.org/" target="_blank" rel="noopener noreferrer"&gt;age&lt;/a&gt;
. Flux has a &lt;code&gt;sops-age&lt;/code&gt; Secret in &lt;code&gt;flux-system&lt;/code&gt; (created once, not committed) and Kustomizations that need secrets set &lt;code&gt;decryption.provider: sops&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; in git: Cloudflare DNS-01, the ARC GitHub token, Grafana admin, registry pull secrets, tunnel token. What is &lt;strong&gt;not&lt;/strong&gt;: the age private key. If you clone that private platform repo you get ciphertext and the public key in &lt;code&gt;.sops.yaml&lt;/code&gt;. That is enough to &lt;em&gt;add&lt;/em&gt; a secret if you have the private key locally; it is not enough to read the live ones.&lt;/p&gt;
&lt;p&gt;This is not Azure Key Vault. A homelab cluster does not need a cloud HSM to stop me committing a PAT. It needs encryption at rest in git and a bootstrap secret that never hits GitHub. The repo documents how SOPS and age are wired up.&lt;/p&gt;
&lt;h2 id="what-flux-does-not-own"&gt;What Flux does not own&lt;a class="anchor ms-1" href="#what-flux-does-not-own" aria-label="Permalink: What Flux does not own"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I keep an ownership map in that private platform repo because GitOps fails the moment two systems apply the same object. Deciding who is allowed to apply has been more useful than any controller version pin. The short version:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Owner&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;This repo (Flux)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Namespaces, MetalLB, cert-manager, Traefik, ARC, Zot, cloudflared, monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App git + CI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Helm releases in &lt;code&gt;develop&lt;/code&gt; / &lt;code&gt;main&lt;/code&gt; / &lt;code&gt;test&lt;/code&gt; (blog chart, TrekRanks, SQL, …)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;k3s / the node&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CoreDNS, local-path, metrics-server, disable bundled Traefik&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Several apps still deploy from Azure DevOps. That is fine. It is &lt;strong&gt;push CI&lt;/strong&gt;, and Flux will not “fix” a Deployment it does not declare. If an old pipeline and a HelmRelease both think they own Grafana, they will fight. I moved monitoring into Flux on purpose so there is one owner.&lt;/p&gt;
&lt;p&gt;k3s itself is not GitOps in this repo either. Node join, versions, and “do not run the bundled Traefik” are documented and applied on the hosts. Flux cannot bootstrap the thing that runs Flux.&lt;/p&gt;
&lt;p&gt;In-cluster &lt;a href="https://docs.renovatebot.com/" target="_blank" rel="noopener noreferrer"&gt;Renovate&lt;/a&gt;
is a small extra: it opens image-bump PRs against that private platform repo. Upgrades become git history too, not SSH-and-hope. One note on Renovate, the Renovate package itself is very noisy, with multiple update PRs raised per day. I have chosen to automerge these, but you may want to consider skipping this package or updating on a schedule.&lt;/p&gt;
&lt;p&gt;I would not start a new cluster by SSHing in and applying twenty manifests. Ingress, certificates, runners, and monitoring are shared, so they belong in git from the start. I would still start a new &lt;em&gt;app&lt;/em&gt; with a pipeline and a Helm chart in &lt;em&gt;that&lt;/em&gt; repo — Flux &lt;code&gt;HelmRelease&lt;/code&gt; is how those charts get onto the cluster, not a replacement for packaging.&lt;/p&gt;
&lt;p&gt;I have not moved every Helm chart into Flux, and I am not pretending that is a moral failing. GitOps earned its place here by making the platform dull. Dull is the goal.&lt;/p&gt;
&lt;p&gt;If you run Kubernetes at home — or you are weighing GitOps against push CI — how do you split &lt;strong&gt;platform&lt;/strong&gt; from &lt;strong&gt;apps&lt;/strong&gt;? Full pull, pipelines only, or a hybrid like mine? I would be interested to hear what works for you in the comments.&lt;/p&gt;
&lt;p&gt;If this is your first step on the same path, these posts are where I started: &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/learning-kubernetes/"&gt;learning Kubernetes&lt;/a&gt;
, &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/deploying-hugo-with-helm/"&gt;deploying with Helm&lt;/a&gt;
, and &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/kubernetes-and-letsencrypt/"&gt;Let&amp;rsquo;s Encrypt on the cluster&lt;/a&gt;
. For a monthly email when I publish something new, &lt;a href="https://blog-dev.funkysi1701.com/newsletter"&gt;subscribe to the newsletter&lt;/a&gt;
.&lt;/p&gt;</description></item><item><title>Pairing with AI in VS Code and Cursor</title><link>https://blog-dev.funkysi1701.com/posts/2026/ai-pair-programming-dotnet/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Fri, 07 Aug 2026 12:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/ai-pair-programming-dotnet/</guid><category term="AI">AI</category><category term="DotNet">DotNet</category><category term="Cursor">Cursor</category><category term="VSCode">VSCode</category><category term="GitHubCopilot">GitHubCopilot</category><category term="Productivity">Productivity</category><category term="Programming">Programming</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/ai-pair-programming-dotnet.png"/><description>&lt;p&gt;On a lot of days my pair programmer is not a person. It is Copilot finishing a method, or Cursor proposing a multi-file edit I still have to own.&lt;/p&gt;
&lt;p&gt;Back in April I covered &lt;a href="https://blog-dev.funkysi1701.com/posts/2026/how-i-use-ai-on-side-projects/"&gt;how I use ChatGPT, Cursor, and Copilot on side projects&lt;/a&gt;
. That was mostly about which tool I reach for. Here I want to talk about what that looks like when the code is .NET—C#, Blazor, Azure Functions—and I am working in VS Code or Cursor.&lt;/p&gt;
&lt;p&gt;I am not claiming a universal workflow. These are habits that have helped me in 2026.&lt;/p&gt;
&lt;h2 id="a-thin-tools-map-so-we-can-move-on"&gt;A thin tools map (so we can move on)&lt;a class="anchor ms-1" href="#a-thin-tools-map-so-we-can-move-on" aria-label="Permalink: A thin tools map (so we can move on)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I still use the same three buckets. The only change is which bucket I reach for when the stack is .NET-heavy.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;What I reach for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framing a design question or learning path&lt;/td&gt;
&lt;td&gt;ChatGPT (browser)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-file change across a solution&lt;/td&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finish this method / test / YAML stanza&lt;/td&gt;
&lt;td&gt;GitHub Copilot in the editor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ldquo;Build AI &lt;em&gt;into&lt;/em&gt; the product&amp;rdquo;&lt;/td&gt;
&lt;td&gt;Separate concern — see &lt;a href="https://blog-dev.funkysi1701.com/posts/using-ai/"&gt;Using AI with .NET and Semantic Kernel&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you already read the April post, you can skim this table and skip ahead. The interesting part is not the brand names; it is what happens after a suggestion appears.&lt;/p&gt;
&lt;p&gt;For vocabulary (RAG, LLM, Copilot, and friends), &lt;a href="https://blog-dev.funkysi1701.com/posts/common-ai-copilot-terms/"&gt;Common AI and Copilot Terms&lt;/a&gt;
is still a useful glossary.&lt;/p&gt;
&lt;h2 id="scenario-unfamiliar-code-that-is-still-mine"&gt;Scenario: unfamiliar code that is still “mine”&lt;a class="anchor ms-1" href="#scenario-unfamiliar-code-that-is-still-mine" aria-label="Permalink: Scenario: unfamiliar code that is still “mine”"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.episodeatlas.com/" target="_blank" rel="noopener noreferrer"&gt;Episode Atlas&lt;/a&gt;
grew the way hobby apps do: Blazor on the front, Azure Functions and Cosmos DB behind, enough moving parts that I forget my own choices. When I come back after a few weeks, I do not want a greenfield tutorial from a chat window. I want something that has &lt;em&gt;seen&lt;/em&gt; the repo.&lt;/p&gt;
&lt;p&gt;In Cursor I ask questions like &amp;ldquo;where does per-user progress get written?&amp;rdquo; or &amp;ldquo;what breaks if this Cosmos query assumes a different partition key?&amp;rdquo; The useful answers cite &lt;em&gt;my&lt;/em&gt; folders and types. The useless ones invent a service layer I never wrote.&lt;/p&gt;
&lt;p&gt;What I do next matters more than the first reply:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open the files it named and confirm they are the real path.&lt;/li&gt;
&lt;li&gt;Diff any proposed edit against how neighbouring code already looks.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotnet build&lt;/code&gt; (and the bit of the app I touched) before I trust it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The productivity win is not that the model &amp;ldquo;knows Episode Atlas.&amp;rdquo; It is that I spend less time remapping a codebase I already wrote.&lt;/p&gt;
&lt;h2 id="scenario-boring-glue-that-still-has-to-be-correct"&gt;Scenario: boring glue that still has to be correct&lt;a class="anchor ms-1" href="#scenario-boring-glue-that-still-has-to-be-correct" aria-label="Permalink: Scenario: boring glue that still has to be correct"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This blog is Hugo plus Azure Static Web Apps, with GitHub Actions and a pile of Node/Python checks. A lot of the work is glue: a layout partial, a workflow step, a Playwright selector after a theme tweak. Copilot is often enough when I already know the shape—finish the next YAML key, sketch a test that matches the one above.&lt;/p&gt;
&lt;p&gt;Cursor earns its keep when the change spans files: &amp;ldquo;update this partial and the smoke test that asserts the heading,&amp;rdquo; or &amp;ldquo;align the SWA config check with what CI runs.&amp;rdquo; Those are exactly the edits where an incomplete suggestion is worse than no suggestion—half-applied config fails in ways that waste an evening.&lt;/p&gt;
&lt;p&gt;On Blazor and Functions work the same pattern shows up as DTOs, mapping, and tests. Copilot is fast at the third similar test. Cursor is better when the DTO, the endpoint, and the client all need to move together.&lt;/p&gt;
&lt;h2 id="scenario-debugging-when-the-model-is-too-confident"&gt;Scenario: debugging when the model is too confident&lt;a class="anchor ms-1" href="#scenario-debugging-when-the-model-is-too-confident" aria-label="Permalink: Scenario: debugging when the model is too confident"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have let an assistant send me on a wild goose chase: wrong package version, an API that never existed, a &amp;ldquo;fix&amp;rdquo; that compiled and still left the bug. A human pair who is sure and wrong is more dangerous than one who says &amp;ldquo;I am guessing.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;When something fails, I try to feed the model the error text and a failing test, not a vague description of what I think is wrong. If the first patch does not change the symptom, I stop re-prompting the same story. I read the stack, check docs, or bisect—same as I would without AI.&lt;/p&gt;
&lt;p&gt;Side projects are a forgiving place to learn that discipline. Production systems are not. Secrets, customer data, and employer code stay out of tools unless policy clearly allows them; I wrote about that boundary in the &lt;a href="https://blog-dev.funkysi1701.com/posts/2026/how-i-use-ai-on-side-projects/"&gt;side projects post&lt;/a&gt;
and I still follow it.&lt;/p&gt;
&lt;h2 id="guardrails-that-actually-saved-me-time"&gt;Guardrails that actually saved me time&lt;a class="anchor ms-1" href="#guardrails-that-actually-saved-me-time" aria-label="Permalink: Guardrails that actually saved me time"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These are the practices that keep AI pairing from becoming AI babysitting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Read every diff you did not type.&lt;/strong&gt; Especially NuGet names, HTTP routes, auth, and anything touching data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer project conventions over &amp;ldquo;cleaner&amp;rdquo; internet defaults.&lt;/strong&gt; If my solution uses one DI style, I reject the rewrite that introduces another for a one-line fix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify against official docs when the call looks unfamiliar.&lt;/strong&gt; Models still hallucinate plausible APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the human review bar.&lt;/strong&gt; I treat agent output like a junior&amp;rsquo;s pull request: helpful when it is right, never something I merge on trust alone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Match depth of context to the task.&lt;/strong&gt; Do not paste half a solution into a browser chat for a one-line rename; do not ask inline Copilot to redesign a Functions + Cosmos flow.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="what-productivity-has-meant-for-me"&gt;What “productivity” has meant for me&lt;a class="anchor ms-1" href="#what-productivity-has-meant-for-me" aria-label="Permalink: What “productivity” has meant for me"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am faster at first drafts and at navigating code I have not opened in a month. I am not faster at &lt;em&gt;skipping&lt;/em&gt; judgment. If anything, the cost of a bad accept is higher because the broken change can look polished.&lt;/p&gt;
&lt;p&gt;That lines up with the longer argument in &lt;a href="https://blog-dev.funkysi1701.com/posts/2026/ai-wont-replace-developers/"&gt;AI Won&amp;rsquo;t Replace Developers, But It Will Redefine Us&lt;/a&gt;
: the work shifts toward directing, reviewing, and verifying. I see that every time I open Cursor or Copilot on Episode Atlas, this blog, or whichever .NET side project I am tinkering with that week.&lt;/p&gt;
&lt;p&gt;If you are doing similar work in C# or Blazor, leave a comment and tell me which tool you reach for first—and what you still refuse to hand to an assistant. I am always curious how other people draw that line.&lt;/p&gt;</description></item><item><title>HTTP QUERY: requests with a body</title><link>https://blog-dev.funkysi1701.com/posts/2026/http-query-method/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Tue, 14 Jul 2026 07:30:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/http-query-method/</guid><category term="HTTP">HTTP</category><category term="QUERY">QUERY</category><category term="RFC10008">RFC10008</category><category term="REST">REST</category><category term="ASP.NETCore">ASP.NETCore</category><category term="APIDesign">APIDesign</category><category term="OpenAPI">OpenAPI</category><category term="DotNet">DotNet</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/http-query-method.png"/><description>&lt;p&gt;For years, API designers have had an awkward choice when a search got too complex for a URL. Stuff it into a &lt;code&gt;GET&lt;/code&gt; query string and hit practical URI length limits, or send a body with &lt;code&gt;POST&lt;/code&gt; and pretend a read-only filter is a write. Neither option felt right.&lt;/p&gt;
&lt;p&gt;In June 2026 the IETF published &lt;a href="https://www.rfc-editor.org/rfc/rfc10008.html" target="_blank" rel="noopener noreferrer"&gt;RFC 10008&lt;/a&gt;
, which defines &lt;strong&gt;QUERY&lt;/strong&gt; — a new HTTP method that is safe and idempotent like &lt;code&gt;GET&lt;/code&gt;, but carries request content like &lt;code&gt;POST&lt;/code&gt;. That long-standing gap finally has a standard answer.&lt;/p&gt;
&lt;h2 id="the-problem-query-solves"&gt;The problem QUERY solves&lt;a class="anchor ms-1" href="#the-problem-query-solves" aria-label="Permalink: The problem QUERY solves"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; is the right semantic for &amp;ldquo;please return something, and I am not asking you to change state.&amp;rdquo; It is safe, idempotent, and cacheable. The catch is where the input lives: in the URI. Complex filters, GraphQL-style documents, Elasticsearch queries, or large multi-field search payloads do not always fit comfortably in a URL. RFC 9110 suggests supporting request targets of at least 8,000 octets, but browsers, proxies, and gateways vary — and overflowing those limits is a practical failure, not a theoretical one.&lt;/p&gt;
&lt;p&gt;URI length is not the only issue. Request URIs — including query-string filters — are far more likely to appear in access logs, browser history, and bookmarks than request bodies. If your search payload contains personal data or other sensitive filters (think GDPR and PII), stuffing it into a &lt;code&gt;GET&lt;/code&gt; URL is often a compliance headache. That is one reason many teams already use &lt;code&gt;POST /search&lt;/code&gt;: the body is less exposed in everyday logging.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; accepts a body with a clear media type, which is perfect for structured input. The catch is what the rest of the HTTP stack is allowed to &lt;em&gt;assume&lt;/em&gt;. HTTP treats &lt;code&gt;POST&lt;/code&gt; as potentially unsafe and non-idempotent: a particular search endpoint can be a pure read in practice, but intermediaries cannot tell that from the method alone. They cannot safely auto-retry a failed &lt;code&gt;POST&lt;/code&gt; the way they can a &lt;code&gt;GET&lt;/code&gt;, and shared caches cannot treat it as a normal repeatable read. Tooling that assumes &amp;ldquo;POST means mutate&amp;rdquo; often groups a catalogue search with create/update traffic — culturally understandable, even when your handler never writes.&lt;/p&gt;
&lt;p&gt;People sometimes tried &lt;code&gt;GET&lt;/code&gt; &lt;em&gt;with&lt;/em&gt; a body. RFC 9110 is blunt about that: content on a &lt;code&gt;GET&lt;/code&gt; has no generally defined semantics, must not change the meaning of the request, and some implementations reject it because of request-smuggling concerns. That was never the answer.&lt;/p&gt;
&lt;p&gt;QUERY is the method that says what we meant all along: &lt;em&gt;process this enclosed content as a query, safely and idempotently, and return the result.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="what-rfc-10008-actually-defines"&gt;What RFC 10008 actually defines&lt;a class="anchor ms-1" href="#what-rfc-10008-actually-defines" aria-label="Permalink: What RFC 10008 actually defines"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From the abstract:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A QUERY requests that the request target process the enclosed content in a safe and idempotent manner and then respond with the result of that processing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Compared with &lt;code&gt;GET&lt;/code&gt;, you are not asking for a representation of the target URI by itself. You are asking the target resource to run a query &lt;em&gt;within its scope&lt;/em&gt;, using the request body and &lt;code&gt;Content-Type&lt;/code&gt; to define that query. Servers must fail the request if &lt;code&gt;Content-Type&lt;/code&gt; is missing or inconsistent with the content.&lt;/p&gt;
&lt;p&gt;Key properties:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Meaning for QUERY&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Safe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The client does not request or expect a state change on the target resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Idempotent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The same request can be retried after a connection failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cacheable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Successful responses may be stored and reused for subsequent QUERY requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Body required for the query&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The query lives in the content, not forced into the URI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Safe&lt;/strong&gt; here is the HTTP meaning: the client is not asking to change the &lt;em&gt;target&lt;/em&gt; resource (for example &lt;code&gt;/api/products&lt;/code&gt;). That does not ban every side effect on the planet. Your server can still increment metrics, write an audit row, or — as the RFC allows — create temporary resources behind &lt;code&gt;Location&lt;/code&gt; / &lt;code&gt;Content-Location&lt;/code&gt; so results can be fetched again with &lt;code&gt;GET&lt;/code&gt;. What intermediaries can assume is that QUERY is a read against the resource you addressed, not a create or update of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Idempotent&lt;/strong&gt; means repeating the same request does not cause &lt;em&gt;additional&lt;/em&gt; side effects beyond what a single successful attempt would cause. If the connection drops after you send a QUERY, a client or proxy can send it again without worrying that the second attempt will “do something twice” — unlike a non-idempotent &lt;code&gt;POST&lt;/code&gt; that might create two orders if retried. It does &lt;em&gt;not&lt;/em&gt; mean the response is frozen forever, and it is not the same as “cache forever.” Like &lt;code&gt;GET&lt;/code&gt;, two identical QUERY calls can return different results if the underlying catalogue changed between them.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;200 OK&lt;/code&gt; means the query was processed successfully and the results are in the response content. That matches how people already think about search endpoints — now with HTTP semantics that intermediaries can trust.&lt;/p&gt;
&lt;h2 id="how-it-compares-to-get-and-post"&gt;How it compares to GET and POST&lt;a class="anchor ms-1" href="#how-it-compares-to-get-and-post" aria-label="Permalink: How it compares to GET and POST"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;GET&lt;/th&gt;
&lt;th&gt;QUERY&lt;/th&gt;
&lt;th&gt;POST (as a &amp;ldquo;search&amp;rdquo;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request body for the query&lt;/td&gt;
&lt;td&gt;No (undefined)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safe (assumed by intermediaries)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not assumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotent (assumed by intermediaries)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not assumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typically cacheable as a read&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not assumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fits large / structured input&lt;/td&gt;
&lt;td&gt;Poorly (URI limits)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keeps filters out of the URI (logs / bookmarks)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-retry after failure&lt;/td&gt;
&lt;td&gt;Safe&lt;/td&gt;
&lt;td&gt;Safe&lt;/td&gt;
&lt;td&gt;Risky&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You still use &lt;code&gt;GET&lt;/code&gt; when the URI alone identifies what you want — and when you are comfortable with that URI being logged. You still use &lt;code&gt;POST&lt;/code&gt; when you are creating or changing something, or when you need a body today and cannot yet rely on QUERY end to end. QUERY is for the awkward middle: &lt;em&gt;read-only operations whose input belongs in a body&lt;/em&gt;, with semantics intermediaries can trust.&lt;/p&gt;
&lt;h2 id="caching-location-and-accept-query"&gt;Caching, Location, and Accept-Query&lt;a class="anchor ms-1" href="#caching-location-and-accept-query" aria-label="Permalink: Caching, Location, and Accept-Query"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Caching QUERY is a bit more work than caching &lt;code&gt;GET&lt;/code&gt;, because the cache key &lt;strong&gt;must&lt;/strong&gt; incorporate the request content (and related metadata). Some caches may normalise insignificant differences when building that key; clients that care can send &lt;code&gt;Cache-Control: no-transform&lt;/code&gt; as advisory guidance.&lt;/p&gt;
&lt;p&gt;The RFC also gives servers a way to hand clients a simpler follow-up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Content-Location&lt;/code&gt;&lt;/strong&gt; — a URI for a resource that holds the &lt;em&gt;results&lt;/em&gt; of this query (temporary storage of what you just got).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Location&lt;/code&gt;&lt;/strong&gt; — a URI for an &lt;em&gt;equivalent resource&lt;/em&gt; that repeats the same query via &lt;code&gt;GET&lt;/code&gt;, without resending the body.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point matters for CDNs and clients that are still catching up: after the first QUERY, subsequent hits can become ordinary &lt;code&gt;GET&lt;/code&gt;s against a stored-query URI.&lt;/p&gt;
&lt;p&gt;Discovery uses the new &lt;strong&gt;&lt;code&gt;Accept-Query&lt;/code&gt;&lt;/strong&gt; response field. A resource can advertise which query media types it understands, for example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;Accept-Query: application/json, application/sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you send an unsupported type, expect &lt;code&gt;415 Unsupported Media Type&lt;/code&gt;, and you may use &lt;code&gt;Accept-Query&lt;/code&gt; (or &lt;code&gt;Accept&lt;/code&gt; on error responses) to learn what to try next.&lt;/p&gt;
&lt;h2 id="cors-and-security-notes"&gt;CORS and security notes&lt;a class="anchor ms-1" href="#cors-and-security-notes" aria-label="Permalink: CORS and security notes"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;QUERY is &lt;strong&gt;not&lt;/strong&gt; a CORS-safelisted method. Browser &lt;code&gt;fetch&lt;/code&gt; calls will trigger a preflight &lt;code&gt;OPTIONS&lt;/code&gt; check, the same way &lt;code&gt;PUT&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; do. That is intentional: you do not want a body-carrying cross-origin method sliding past CORS just because it is &amp;ldquo;read-like.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Putting filters in the QUERY body already helps versus a fat &lt;code&gt;GET&lt;/code&gt; query string — less sensitive material in URIs that get logged or bookmarked. That benefit disappears if you then mint temporary &lt;code&gt;Location&lt;/code&gt; or &lt;code&gt;Content-Location&lt;/code&gt; URIs that echo those filters into the path or query. The RFC warns those URIs should not include sensitive portions of the original request content.&lt;/p&gt;
&lt;h2 id="using-query-with-aspnet-core"&gt;Using QUERY with ASP.NET Core&lt;a class="anchor ms-1" href="#using-query-with-aspnet-core" aria-label="Permalink: Using QUERY with ASP.NET Core"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You do not need to wait for .NET 11 to try QUERY on the server. ASP.NET Core routing already accepts arbitrary method strings via &lt;code&gt;MapMethods&lt;/code&gt;, and recent releases expose &lt;code&gt;HttpMethods.Query&lt;/code&gt; (with &lt;code&gt;HttpMethods.IsQuery&lt;/code&gt;) so you do not have to sprinkle the literal &lt;code&gt;&amp;quot;QUERY&amp;quot;&lt;/code&gt; everywhere:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app.MapMethods(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/products/search&amp;#34;&lt;/span&gt;, [HttpMethods.Query], (ProductSearchRequest request) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; results = ProductCatalogue.Search(request);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; TypedResults.Ok(results);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A thin helper keeps call sites readable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HttpQueryExtensions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; IEndpointConventionBuilder MapQuery(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; IEndpointRouteBuilder endpoints,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; pattern,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Delegate handler) =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; endpoints.MapMethods(pattern, [HttpMethods.Query], handler);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What landed later is OpenAPI documentation. QUERY became a first-class Path Item operation in &lt;strong&gt;OpenAPI 3.2&lt;/strong&gt;. In &lt;strong&gt;.NET 11&lt;/strong&gt;, ASP.NET Core&amp;rsquo;s document generator recognises QUERY when you opt into that version — so Swagger / client generators see a proper &lt;code&gt;query&lt;/code&gt; operation instead of an oddball method string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;builder.Services.AddOpenApi(options =&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_2;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With OpenAPI 3.2, the operation appears as a sibling of &lt;code&gt;get&lt;/code&gt; / &lt;code&gt;post&lt;/code&gt;. On older document versions, generators may fall back to an extension such as &lt;code&gt;x-oai-additionalOperations&lt;/code&gt; so the endpoint is not silently dropped. Until you are on that stack, you can still ship QUERY endpoints and document them by hand if needed.&lt;/p&gt;
&lt;p&gt;Clients can call it with curl once the server understands the verb:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X QUERY https://api.example.com/api/products/search &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;category&amp;#34;:&amp;#34;laptops&amp;#34;,&amp;#34;minPrice&amp;#34;:500,&amp;#34;tags&amp;#34;:[&amp;#34;ultrabook&amp;#34;,&amp;#34;16gb&amp;#34;]}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="what-to-watch-for-in-the-real-world"&gt;What to watch for in the real world&lt;a class="anchor ms-1" href="#what-to-watch-for-in-the-real-world" aria-label="Permalink: What to watch for in the real world"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Standards land before the entire ecosystem catches up. Worth checking before you announce QUERY as your public search API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Proxies, API gateways, and WAFs&lt;/strong&gt; — some still allow-list only a handful of methods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser and CDN behaviour&lt;/strong&gt; — caching QUERY responses is more complex than &lt;code&gt;GET&lt;/code&gt;; temporary &lt;code&gt;Location&lt;/code&gt; URIs help.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client libraries and OpenAPI codegen&lt;/strong&gt; — support is improving, but pin versions carefully.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logs and analytics&lt;/strong&gt; — make sure QUERY is counted as a read, not lumped in with mutating &lt;code&gt;POST&lt;/code&gt;s.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For service-to-service APIs under your control, QUERY is already useful. For public browser-facing APIs, validate your edge stack and CORS setup first.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why this matters&lt;a class="anchor ms-1" href="#why-this-matters" aria-label="Permalink: Why this matters"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HTTP methods are how we communicate intent to every hop between the client and your app. When intent and method disagree — a read that intermediaries cannot assume from &lt;code&gt;POST&lt;/code&gt;, or a body bolted onto &lt;code&gt;GET&lt;/code&gt; — caches, retries, and security tooling all have to guess.&lt;/p&gt;
&lt;p&gt;QUERY closes that gap with a small, carefully designed verb: safe, idempotent, cacheable, and allowed to carry a body. After more than a decade of drafts, &lt;a href="https://www.rfc-editor.org/rfc/rfc10008.html" target="_blank" rel="noopener noreferrer"&gt;RFC 10008&lt;/a&gt;
makes that contract official. If you maintain search or filter endpoints that never belonged in a URI, it is time to start planning for QUERY — especially on service-to-service paths you control, before you bet a public browser API on every gateway in the path understanding the verb.&lt;/p&gt;</description></item><item><title>.NET 5 to 10: features per release</title><link>https://blog-dev.funkysi1701.com/posts/2026/dotnet-5-to-10-features/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 29 Jun 2026 18:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/dotnet-5-to-10-features/</guid><category term="DotNet">DotNet</category><category term=".NET5">.NET5</category><category term=".NET6">.NET6</category><category term=".NET7">.NET7</category><category term=".NET8">.NET8</category><category term=".NET9">.NET9</category><category term=".NET10">.NET10</category><category term="C-Sharp">C-Sharp</category><category term="Blazor">Blazor</category><category term="ASP.NETCore">ASP.NETCore</category><category term="VisualStudio">VisualStudio</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/dotnet-5-to-10-features.png"/><description>&lt;p&gt;Every November, &lt;a href="https://www.dotnetconf.net/" target="_blank" rel="noopener noreferrer"&gt;.NET Conf&lt;/a&gt;
brings a new major release of .NET. If you have been building on the platform since the early 2020s, you have watched the ecosystem move from &amp;ldquo;.NET Core&amp;rdquo; to a single, unified .NET — and pick up minimal APIs, Native AOT, Blazor full stack, Aspire, and a steady stream of C# language improvements along the way.&lt;/p&gt;
&lt;p&gt;This post is a guided tour of what landed in each major version from &lt;strong&gt;.NET 5&lt;/strong&gt; through &lt;strong&gt;.NET 10&lt;/strong&gt;. It is not exhaustive — Microsoft ships hundreds of changes per release — but it should help you remember which version introduced what, and where to dig deeper.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Released&lt;/th&gt;
&lt;th&gt;Support type&lt;/th&gt;
&lt;th&gt;C# version&lt;/th&gt;
&lt;th&gt;Status (June 2026)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;.NET 5&lt;/td&gt;
&lt;td&gt;Nov 2020&lt;/td&gt;
&lt;td&gt;STS (18 months)&lt;/td&gt;
&lt;td&gt;C# 9&lt;/td&gt;
&lt;td&gt;End of support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET 6&lt;/td&gt;
&lt;td&gt;Nov 2021&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LTS&lt;/strong&gt; (3 years)&lt;/td&gt;
&lt;td&gt;C# 10&lt;/td&gt;
&lt;td&gt;End of support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET 7&lt;/td&gt;
&lt;td&gt;Nov 2022&lt;/td&gt;
&lt;td&gt;STS (18 months)&lt;/td&gt;
&lt;td&gt;C# 11&lt;/td&gt;
&lt;td&gt;End of support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET 8&lt;/td&gt;
&lt;td&gt;Nov 2023&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LTS&lt;/strong&gt; (3 years)&lt;/td&gt;
&lt;td&gt;C# 12&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET 9&lt;/td&gt;
&lt;td&gt;Nov 2024&lt;/td&gt;
&lt;td&gt;STS (2 years)&lt;/td&gt;
&lt;td&gt;C# 13&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET 10&lt;/td&gt;
&lt;td&gt;Nov 2025&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;LTS&lt;/strong&gt; (3 years)&lt;/td&gt;
&lt;td&gt;C# 14&lt;/td&gt;
&lt;td&gt;Current LTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET 11&lt;/td&gt;
&lt;td&gt;Nov 2026&lt;/td&gt;
&lt;td&gt;Preview&lt;/td&gt;
&lt;td&gt;C# 15&lt;/td&gt;
&lt;td&gt;Preview Until Nov 2026 then STS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For the full release schedule and patch versions, see the official &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/releases-and-support" target="_blank" rel="noopener noreferrer"&gt;.NET releases&lt;/a&gt;
documentation.&lt;/p&gt;
&lt;h2 id="net-5--november-2020"&gt;.NET 5 — November 2020&lt;a class="anchor ms-1" href="#net-5--november-2020" aria-label="Permalink: .NET 5 — November 2020"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET 5 was the first release after the &amp;ldquo;Core&amp;rdquo; branding was dropped. Version 4.x was skipped deliberately so nobody confused it with .NET Framework 4.x. The message was clear: this is the main implementation of .NET going forward, not a side project.&lt;/p&gt;
&lt;h3 id="platform-and-runtime"&gt;Platform and runtime&lt;a class="anchor ms-1" href="#platform-and-runtime" aria-label="Permalink: Platform and runtime"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unified vision&lt;/strong&gt; — one BCL and runtime targeting cloud, desktop, web, mobile, and IoT (the full unification completed in .NET 6).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single-file applications&lt;/strong&gt; and improved &lt;strong&gt;app trimming&lt;/strong&gt; for smaller deployments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ClickOnce&lt;/strong&gt; publishing for .NET (Windows).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ARM64&lt;/strong&gt; support on Windows.&lt;/li&gt;
&lt;li&gt;Performance work across the runtime, GC, and networking stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="c-9"&gt;C# 9&lt;a class="anchor ms-1" href="#c-9" aria-label="Permalink: C# 9"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;C# 9 shipped alongside .NET 5 and brought several features that are now everyday tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Records&lt;/strong&gt; — reference types with value-based equality and &lt;code&gt;with&lt;/code&gt; expressions for non-destructive mutation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Init-only setters&lt;/strong&gt; — immutable object initialisation without verbose constructors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Top-level statements&lt;/strong&gt; — less boilerplate in small console apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pattern matching&lt;/strong&gt; — relational patterns (&lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;not&lt;/code&gt;) and improved &lt;code&gt;switch&lt;/code&gt; expressions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source generators&lt;/strong&gt; — compile-time code generation (the foundation for later JSON and logging generators).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="libraries-and-frameworks"&gt;Libraries and frameworks&lt;a class="anchor ms-1" href="#libraries-and-frameworks" aria-label="Permalink: Libraries and frameworks"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EF Core 5&lt;/strong&gt; — split queries, table-per-entity (TPE) mapping, and Cosmos DB improvements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ASP.NET Core&lt;/strong&gt; in the 5.x line — gRPC improvements, HTTP/2 and HTTP/3 groundwork, and Blazor WebAssembly performance gains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System.Text.Json&lt;/strong&gt; — constructor and parameterised constructor support, &lt;code&gt;[JsonIgnore]&lt;/code&gt; on properties, and better options for ignoring cycles.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;.NET 5 reached end of support in May 2022. If you still have projects on it, plan an upgrade — there are no security patches.&lt;/p&gt;
&lt;h2 id="net-6--november-2021-lts"&gt;.NET 6 — November 2021 (LTS)&lt;a class="anchor ms-1" href="#net-6--november-2021-lts" aria-label="Permalink: .NET 6 — November 2021 (LTS)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET 6 delivered on the unification promise: one SDK, one base class library, and one runtime across mobile, desktop, IoT, and cloud. It was an &lt;strong&gt;LTS&lt;/strong&gt; release and became the baseline many teams standardised on.&lt;/p&gt;
&lt;h3 id="developer-experience"&gt;Developer experience&lt;a class="anchor ms-1" href="#developer-experience" aria-label="Permalink: Developer experience"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hot reload&lt;/strong&gt; in Visual Studio 2022 and &lt;code&gt;dotnet watch&lt;/code&gt; — edit code and see changes without a full restart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal APIs&lt;/strong&gt; — build HTTP APIs with far less ceremony than traditional controllers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDK workloads&lt;/strong&gt; — optional components (MAUI, Blazor WebAssembly AOT, and others) install via &lt;code&gt;dotnet workload&lt;/code&gt; instead of bloating the default SDK.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Project templates modernised&lt;/strong&gt; — top-level statements, file-scoped namespaces, nullable reference types, and &lt;code&gt;async Main&lt;/code&gt; in new projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="c-10"&gt;C# 10&lt;a class="anchor ms-1" href="#c-10" aria-label="Permalink: C# 10"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;global using&lt;/code&gt; directives&lt;/li&gt;
&lt;li&gt;File-scoped namespaces&lt;/li&gt;
&lt;li&gt;Record structs&lt;/li&gt;
&lt;li&gt;Improved interpolated strings and constant interpolated strings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AsyncMethodBuilder&lt;/code&gt; attribute for custom async method builders&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="runtime-and-libraries"&gt;Runtime and libraries&lt;a class="anchor ms-1" href="#runtime-and-libraries" aria-label="Permalink: Runtime and libraries"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DateOnly&lt;/strong&gt; and &lt;strong&gt;TimeOnly&lt;/strong&gt; — separate date and time types without carrying a full &lt;code&gt;DateTime&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PriorityQueue&amp;lt;TElement, TPriority&amp;gt;&lt;/strong&gt; — a built-in min-heap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System.Text.Json&lt;/strong&gt; source generator, writeable &lt;strong&gt;JsonNode&lt;/strong&gt; DOM, and &lt;code&gt;IAsyncEnumerable&lt;/code&gt; serialisation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP/3&lt;/strong&gt; preview support via QUIC.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt; metrics APIs in &lt;code&gt;System.Diagnostics.Metrics&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FileStream&lt;/strong&gt; rewrite for better async performance on Windows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arm64&lt;/strong&gt; support for macOS (&amp;ldquo;Apple Silicon&amp;rdquo;) and Windows, with side-by-side x64 and Arm64 installs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aspnet-core-6"&gt;ASP.NET Core 6&lt;a class="anchor ms-1" href="#aspnet-core-6" aria-label="Permalink: ASP.NET Core 6"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Minimal APIs as a first-class style for microservices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blazor&lt;/strong&gt; components renderable from JavaScript; AOT compilation for Blazor WebAssembly.&lt;/li&gt;
&lt;li&gt;Improved gRPC and SignalR performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="net-maui"&gt;.NET MAUI&lt;a class="anchor ms-1" href="#net-maui" aria-label="Permalink: .NET MAUI"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;.NET Multi-platform App UI arrived in preview with .NET 6 — one codebase for Android, iOS, macOS, and Windows native apps. GA followed in 2022.&lt;/p&gt;
&lt;p&gt;.NET 6 LTS ended in November 2024.&lt;/p&gt;
&lt;h2 id="net-7--november-2022"&gt;.NET 7 — November 2022&lt;a class="anchor ms-1" href="#net-7--november-2022" aria-label="Permalink: .NET 7 — November 2022"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET 7 was a &lt;strong&gt;standard-term support&lt;/strong&gt; release focused heavily on performance, cloud-native patterns, and polishing the developer experience. I wrote about &lt;a href="https://blog-dev.funkysi1701.com/posts/2022/dotnet7/"&gt;upgrading to .NET 7&lt;/a&gt;
at the time — mostly smooth, with a few surprises around AutoMapper and EF Core triggers.&lt;/p&gt;
&lt;h3 id="c-11"&gt;C# 11&lt;a class="anchor ms-1" href="#c-11" aria-label="Permalink: C# 11"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Raw string literals&lt;/strong&gt; — multi-line strings without escape-character pain.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;required&lt;/code&gt; members&lt;/strong&gt; — enforce initialisation at compile time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generic math&lt;/strong&gt; — static abstract interface members for numeric generics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;List patterns&lt;/strong&gt; — &lt;code&gt;if (list is [var first, .., var last])&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UTF-8 string literals&lt;/strong&gt; — &lt;code&gt;u8&lt;/code&gt; suffix for byte-oriented APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aspnet-core-7"&gt;ASP.NET Core 7&lt;a class="anchor ms-1" href="#aspnet-core-7" aria-label="Permalink: ASP.NET Core 7"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rate limiting middleware&lt;/strong&gt; — built-in protection against abuse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output caching&lt;/strong&gt; — cache entire responses at the framework level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal API improvements&lt;/strong&gt; — route groups, typed results, and filters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native AOT&lt;/strong&gt; for console apps — smaller, faster-starting binaries (ASP.NET Core AOT came in .NET 8).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="performance-and-tooling"&gt;Performance and tooling&lt;a class="anchor ms-1" href="#performance-and-tooling" aria-label="Permalink: Performance and tooling"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Continued JIT and GC improvements; &lt;strong&gt;Native AOT&lt;/strong&gt; publishing matured.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regex source generator&lt;/strong&gt; for compile-time pattern compilation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Library multi-targeting&lt;/strong&gt; and package validation tooling for NuGet authors.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;.NET 7 reached end of support in May 2024.&lt;/p&gt;
&lt;h2 id="net-8--november-2023-lts"&gt;.NET 8 — November 2023 (LTS)&lt;a class="anchor ms-1" href="#net-8--november-2023-lts" aria-label="Permalink: .NET 8 — November 2023 (LTS)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET 8 is the current-generation &lt;strong&gt;LTS&lt;/strong&gt; baseline for many production workloads (supported until November 2026). It is the release where cloud-native tooling, Native AOT for web apps, and modern C# really clicked together.&lt;/p&gt;
&lt;h3 id="c-12"&gt;C# 12&lt;a class="anchor ms-1" href="#c-12" aria-label="Permalink: C# 12"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primary constructors&lt;/strong&gt; — declare constructor parameters on the type declaration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collection expressions&lt;/strong&gt; — &lt;code&gt;[1, 2, 3]&lt;/code&gt; syntax for arrays, spans, and lists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alias any type&lt;/strong&gt; — &lt;code&gt;using Point = (int X, int Y);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Default lambda parameters&lt;/strong&gt; — optional parameters in lambdas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aspnet-core-8"&gt;ASP.NET Core 8&lt;a class="anchor ms-1" href="#aspnet-core-8" aria-label="Permalink: ASP.NET Core 8"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blazor full stack&lt;/strong&gt; — server-side rendering, streaming rendering, and enhanced interactivity models in one framework.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native AOT&lt;/strong&gt; for ASP.NET Core — ahead-of-time compilation for web workloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keyed dependency injection&lt;/strong&gt; — register and resolve multiple implementations of the same interface by key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frozen collections&lt;/strong&gt; — optimised read-only collections for hot paths.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TimeProvider&lt;/strong&gt; — abstract time for testable code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="net-aspire-now-just-called-aspire"&gt;.NET Aspire (now just called Aspire)&lt;a class="anchor ms-1" href="#net-aspire-now-just-called-aspire" aria-label="Permalink: .NET Aspire (now just called Aspire)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2024/aspire/"&gt;.NET Aspire&lt;/a&gt;
launched in preview with .NET 8 — an opinionated stack for observable, distributed applications. AppHost orchestrates dependencies; ServiceDefaults wire up OpenTelemetry, health checks, and resilience. I have been using it for local microservice development and it removes a lot of port-and-connection-string drudgery. In 2025 .Net Aspire was rebranded to Aspire and version 13 was released (don&amp;rsquo;t ask why 13, but I believe it was so it was no longer tied to .NET releases)&lt;/p&gt;
&lt;h3 id="other-highlights"&gt;Other highlights&lt;a class="anchor ms-1" href="#other-highlights" aria-label="Permalink: Other highlights"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EF Core 8&lt;/strong&gt; — complex types, primitive collections, JSON column mapping, &lt;code&gt;HierarchyId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NuGet Audit&lt;/strong&gt; — warnings when packages have known vulnerabilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows Forms and WPF&lt;/strong&gt; improvements — DPI, data binding, and hardware acceleration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source generators&lt;/strong&gt; for COM interop and configuration binding.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="net-9--november-2024"&gt;.NET 9 — November 2024&lt;a class="anchor ms-1" href="#net-9--november-2024" aria-label="Permalink: .NET 9 — November 2024"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET 9 continued the annual cadence with performance, AI integration, and polish across the stack. I covered several highlights in my &lt;a href="https://blog-dev.funkysi1701.com/posts/2024/dotnet9/"&gt;.NET 9 post&lt;/a&gt;
when it shipped.&lt;/p&gt;
&lt;h3 id="c-13"&gt;C# 13&lt;a class="anchor ms-1" href="#c-13" aria-label="Permalink: C# 13"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;params&lt;/code&gt; collections&lt;/strong&gt; — &lt;code&gt;params&lt;/code&gt; works with &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;ReadOnlySpan&amp;lt;T&amp;gt;&lt;/code&gt;, and collection types beyond arrays.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;lock&lt;/code&gt; on &lt;code&gt;System.Object&lt;/code&gt;&lt;/strong&gt; — the &lt;code&gt;lock&lt;/code&gt; statement can use more types safely.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partial properties and indexers&lt;/strong&gt; — split declarations across files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escape sequence &lt;code&gt;\e&lt;/code&gt;&lt;/strong&gt; for the ESC character.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="platform-and-libraries"&gt;Platform and libraries&lt;a class="anchor ms-1" href="#platform-and-libraries" aria-label="Permalink: Platform and libraries"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt; — runtime, GC, and JIT improvements across the board; the team publishes detailed benchmark posts each release.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI abstractions&lt;/strong&gt; — &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt; packages unify access to language models and embeddings from different providers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NuGet Audit&lt;/strong&gt; defaults to reporting &lt;strong&gt;transitive&lt;/strong&gt; dependency vulnerabilities (opt back to direct-only with &lt;code&gt;&amp;lt;NuGetAuditMode&amp;gt;direct&amp;lt;/NuGetAuditMode&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenTelemetry&lt;/strong&gt; enhancements and better observability defaults.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aspnet-core-9-and-blazor"&gt;ASP.NET Core 9 and Blazor&lt;a class="anchor ms-1" href="#aspnet-core-9-and-blazor" aria-label="Permalink: ASP.NET Core 9 and Blazor"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;MapStaticAssets()&lt;/code&gt;&lt;/strong&gt; — fingerprinted, cache-friendly static files replace &lt;code&gt;UseStaticFiles()&lt;/code&gt; for Blazor and modern web apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blazor&lt;/strong&gt; component state persistence, improved form handling, and better WebAssembly performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.NET Aspire 9&lt;/strong&gt; — dashboard improvements, deployment tooling, and more community integrations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;.NET 9 is &lt;strong&gt;standard-term support&lt;/strong&gt; until November 2026.&lt;/p&gt;
&lt;h2 id="net-10--november-2025-lts"&gt;.NET 10 — November 2025 (LTS)&lt;a class="anchor ms-1" href="#net-10--november-2025-lts" aria-label="Permalink: .NET 10 — November 2025 (LTS)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;.NET 10 is the current &lt;strong&gt;LTS&lt;/strong&gt; release (supported until November 2028). If you are choosing a version for a new project in 2026, this is the long-term bet.&lt;/p&gt;
&lt;h3 id="c-14"&gt;C# 14&lt;a class="anchor ms-1" href="#c-14" aria-label="Permalink: C# 14"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Field-backed properties&lt;/strong&gt; — use the &lt;code&gt;field&lt;/code&gt; keyword when you outgrow auto-properties.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;nameof&lt;/code&gt; for unbound generics&lt;/strong&gt; — e.g. &lt;code&gt;nameof(List&amp;lt;&amp;gt;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implicit &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; conversions&lt;/strong&gt; — less ceremony at API boundaries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ref&lt;/code&gt;/&lt;code&gt;out&lt;/code&gt;/&lt;code&gt;in&lt;/code&gt; in lambdas&lt;/strong&gt; without explicit parameter types.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extension blocks&lt;/strong&gt; — static extension methods and properties in a dedicated syntax.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Null-conditional assignment&lt;/strong&gt; (&lt;code&gt;?.=&lt;/code&gt;) and user-defined compound assignment operators.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="runtime-and-libraries-1"&gt;Runtime and libraries&lt;a class="anchor ms-1" href="#runtime-and-libraries-1" aria-label="Permalink: Runtime and libraries"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JIT improvements&lt;/strong&gt; — better inlining, devirtualisation, and loop inversion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVX10.2&lt;/strong&gt; support for SIMD workloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native AOT&lt;/strong&gt; enhancements and improved struct argument code generation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON&lt;/strong&gt; — stricter serialisation options, &lt;code&gt;PipeReader&lt;/code&gt; support, duplicate property handling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Post-quantum cryptography&lt;/strong&gt; — expanded ML-DSA and related APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WebSocketStream&lt;/code&gt;&lt;/strong&gt; — simpler WebSocket usage; TLS 1.3 on macOS clients.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sdk-and-tooling"&gt;SDK and tooling&lt;a class="anchor ms-1" href="#sdk-and-tooling" aria-label="Permalink: SDK and tooling"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;dotnet test&lt;/code&gt;&lt;/strong&gt; integrates &lt;strong&gt;Microsoft.Testing.Platform&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container images from console apps&lt;/strong&gt; without a separate Dockerfile in simple scenarios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;dotnet tool exec&lt;/code&gt;&lt;/strong&gt; for one-shot tool runs.&lt;/li&gt;
&lt;li&gt;Native shell tab-completion scripts for the CLI.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aspnet-core-10-and-blazor"&gt;ASP.NET Core 10 and Blazor&lt;a class="anchor ms-1" href="#aspnet-core-10-and-blazor" aria-label="Permalink: ASP.NET Core 10 and Blazor"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Blazor WebAssembly &lt;strong&gt;preloading&lt;/strong&gt; and automatic memory pool eviction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Passkey support&lt;/strong&gt; for ASP.NET Core Identity.&lt;/li&gt;
&lt;li&gt;OpenAPI and minimal API improvements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One breaking change caught me when upgrading a Blazor WebAssembly app: &lt;strong&gt;HttpClient response streaming is enabled by default&lt;/strong&gt;, which broke synchronous &lt;code&gt;Stream.Read&lt;/code&gt; calls in generated API clients. I wrote about the fix in &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/blazor-and-dotnet10/"&gt;Blazor and .NET 10&lt;/a&gt;
— either opt out with &lt;code&gt;&amp;lt;WasmEnableStreamingResponse&amp;gt;false&amp;lt;/WasmEnableStreamingResponse&amp;gt;&lt;/code&gt; or move to async reads.&lt;/p&gt;
&lt;h3 id="ef-core-10-maui-and-desktop"&gt;EF Core 10, MAUI, and desktop&lt;a class="anchor ms-1" href="#ef-core-10-maui-and-desktop" aria-label="Permalink: EF Core 10, MAUI, and desktop"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;EF Core 10&lt;/strong&gt; — named query filters, Cosmos DB improvements, and LINQ performance work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.NET MAUI 10&lt;/strong&gt; — multi-file MediaPicker, WebView interception, Android API 35/36 support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows Forms and WPF&lt;/strong&gt; — continued quality and Fluent-style updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-choose-a-version-in-2026"&gt;How to choose a version in 2026&lt;a class="anchor ms-1" href="#how-to-choose-a-version-in-2026" aria-label="Permalink: How to choose a version in 2026"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;New production app, minimise upgrade churn&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 10 LTS&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing app on .NET 8 LTS with no pressing need&lt;/td&gt;
&lt;td&gt;Stay on 8 until you are ready; both 8 and 10 are supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Experimenting with the latest features&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 9&lt;/strong&gt; or &lt;strong&gt;.NET 10&lt;/strong&gt; — check STS/LTS dates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy maintenance&lt;/td&gt;
&lt;td&gt;Upgrade anything still on 5, 6, or 7 — they are all end-of-life&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Microsoft&amp;rsquo;s guidance has shifted over the years: LTS releases (6, 8, 10) alternate with STS releases (5, 7, 9) on an annual November cadence. STS now lasts two years; LTS lasts three. Remember there is no difference in quality between a LTS and STS release, it is only the support window that is different.&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further reading&lt;a class="anchor ms-1" href="#further-reading" aria-label="Permalink: Further reading"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="official-docs"&gt;Official docs&lt;a class="anchor ms-1" href="#official-docs" aria-label="Permalink: Official docs"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/download" target="_blank" rel="noopener noreferrer"&gt;.NET download&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/" target="_blank" rel="noopener noreferrer"&gt;What&amp;rsquo;s new in each .NET version&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history" target="_blank" rel="noopener noreferrer"&gt;C# language version history&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="on-this-blog"&gt;On this blog&lt;a class="anchor ms-1" href="#on-this-blog" aria-label="Permalink: On this blog"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2024/dotnet9/"&gt;.NET 9&lt;/a&gt;
and &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/blazor-and-dotnet10/"&gt;Blazor and .NET 10&lt;/a&gt;
— deeper dives from recent releases&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/whats-new-csharp/"&gt;What&amp;rsquo;s new with C#&lt;/a&gt;
— language features that shipped alongside the runtime&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2024/aspire/"&gt;Getting started with Aspire&lt;/a&gt;
and &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/adding-elasticsearch-with-aspire/"&gt;Adding Elasticsearch with Aspire&lt;/a&gt;
— cloud-native .NET after you pick a target framework&lt;/li&gt;
&lt;li&gt;Prefer a short curated path? See &lt;a href="https://blog-dev.funkysi1701.com/start-here/"&gt;Start Here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have been on the platform since .NET 5, you have lived through the best decade of .NET since the original framework shipped.&lt;/p&gt;</description></item><item><title>Portfolio: Azure, AI, and dev tools</title><link>https://blog-dev.funkysi1701.com/projects/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 22 Jun 2026 21:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/projects/</guid><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/projects/semantic-search.png"/><description>&lt;h2 id="featured-projects-heading"&gt;Featured Projects&lt;/h2&gt;
&lt;p class="projects-featured-intro"&gt;Three live Azure workloads I own end-to-end — CI/CD static delivery, AI-backed search on real content, and full-stack Blazor with actual users. &lt;/p&gt;
&lt;div class="projects-featured" role="region" aria-labelledby="featured-projects-heading"&gt;
&lt;div class="projects-featured-grid"&gt;
&lt;article class="projects-featured-card"&gt;
&lt;h3 class="projects-featured-title"&gt;&lt;a href="https://www.episodeatlas.com" target="_blank" rel="noopener noreferrer"&gt;🔧 Episode Atlas&lt;/a&gt;&lt;/h3&gt;
&lt;p class="projects-featured-hook"&gt;Track TV progress at scale with per-user state and cloud-backed data.&lt;/p&gt;
&lt;a href="https://www.episodeatlas.com" target="_blank" rel="noopener noreferrer"&gt;&lt;img src="https://blog-dev.funkysi1701.com/images/projects/episode-atlas.png" alt="Episode Atlas: TV episode tracking app" width="800" height="450" loading="lazy"&gt;&lt;/a&gt;
&lt;p class="projects-featured-meta"&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Per-user episode lists, “last watched” markers, and curated metadata links — so long-running franchises stay manageable.&lt;/p&gt;
&lt;p class="projects-featured-meta"&gt;&lt;strong&gt;Tech used:&lt;/strong&gt; .NET Blazor, Azure Static Web Apps, Azure Functions, Cosmos DB, GitHub auth.&lt;/p&gt;
&lt;p class="projects-featured-impressive"&gt;&lt;strong&gt;Why it’s impressive:&lt;/strong&gt; Demonstrates scalable data modelling and cloud-first architecture for user-driven applications.&lt;/p&gt;
&lt;div class="projects-signals" role="group" aria-label="Engineering signals"&gt;&lt;span class="projects-signal"&gt;Deployed to Azure&lt;/span&gt;&lt;span class="projects-signal"&gt;Implements authentication / APIs / caching&lt;/span&gt;&lt;/div&gt;
&lt;p class="projects-featured-outcome"&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Turned a personal “where was I in 900+ episodes?” problem into a maintainable multi-tenant pattern for identity, persistence, and UX for ongoing shows.&lt;/p&gt;
&lt;p class="projects-featured-links"&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://www.episodeatlas.com" target="_blank" rel="noopener noreferrer"&gt;episodeatlas.com&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;
&lt;article class="projects-featured-card"&gt;
&lt;h3 class="projects-featured-title"&gt;&lt;a href="https://search.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;🔧 Semantic Search&lt;/a&gt;&lt;/h3&gt;
&lt;p class="projects-featured-hook"&gt;AI-powered natural language search for discovering content beyond keywords.&lt;/p&gt;
&lt;a href="https://search.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;&lt;img src="https://blog-dev.funkysi1701.com/images/projects/semantic-search.png" alt="Semantic Search: natural-language blog search" width="800" height="450" loading="lazy"&gt;&lt;/a&gt;
&lt;p class="projects-featured-meta"&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Accepts conversational queries, retrieves meaningfully related posts, and surfaces them alongside the site’s JSON index search.&lt;/p&gt;
&lt;p class="projects-featured-meta"&gt;&lt;strong&gt;Tech used:&lt;/strong&gt; .NET, Azure, Cosmos DB, OpenAI.&lt;/p&gt;
&lt;p class="projects-featured-impressive"&gt;&lt;strong&gt;Why it’s impressive:&lt;/strong&gt; Showcases practical LLM integration in .NET beyond basic chat interfaces.&lt;/p&gt;
&lt;div class="projects-signals" role="group" aria-label="Engineering signals"&gt;&lt;span class="projects-signal"&gt;Deployed to Azure&lt;/span&gt;&lt;span class="projects-signal"&gt;Designed with clean architecture&lt;/span&gt;&lt;/div&gt;
&lt;p class="projects-featured-outcome"&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Explored how vector-style retrieval and operational concerns (cost, latency, content freshness) fit next to a static Hugo site without compromising the authoring workflow.&lt;/p&gt;
&lt;p class="projects-featured-links"&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://search.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;search.funkysi1701.com&lt;/a&gt; · linked from &lt;a href="https://blog-dev.funkysi1701.com/search/"&gt;Search&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;
&lt;article class="projects-featured-card"&gt;
&lt;h3 class="projects-featured-title"&gt;&lt;a href="https://www.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;🔧 Blog Platform&lt;/a&gt;&lt;/h3&gt;
&lt;p class="projects-featured-hook"&gt;Technical blog on Azure with CI/CD—fast static pages, search, and comments without babysitting an app server.&lt;/p&gt;
&lt;a href="https://www.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;&lt;img src="https://blog-dev.funkysi1701.com/images/projects/blog.png" alt="Blog homepage: navigation and latest posts" width="800" height="450" loading="lazy"&gt;&lt;/a&gt;
&lt;p class="projects-featured-meta"&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Publishes posts, generates RSS and a JSON search index, integrates Giscus for discussions, and deploys on every merge.&lt;/p&gt;
&lt;p class="projects-featured-meta"&gt;&lt;strong&gt;Tech used:&lt;/strong&gt; Hugo, GitHub Actions, Azure Static Web Apps, Giscus.&lt;/p&gt;
&lt;p class="projects-featured-impressive"&gt;&lt;strong&gt;Why it’s impressive:&lt;/strong&gt; Highlights modern static-first architecture with automated deployment pipelines.&lt;/p&gt;
&lt;div class="projects-signals" role="group" aria-label="Engineering signals"&gt;&lt;span class="projects-signal"&gt;Deployed to Azure&lt;/span&gt;&lt;span class="projects-signal"&gt;CI/CD via GitHub Actions&lt;/span&gt;&lt;/div&gt;
&lt;p class="projects-featured-outcome"&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Demonstrates a production-shaped static pipeline — preview builds, repeatable deploys, and content-driven SEO — suitable as a reference architecture for docs and marketing sites.&lt;/p&gt;
&lt;p class="projects-featured-links"&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://github.com/funkysi1701/funkysi1701.github.io" target="_blank" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;
&lt;/article&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="-cloud--production-systems"&gt;💼 Cloud &amp; Production Systems&lt;a class="anchor ms-1" href="#-cloud--production-systems" aria-label="Permalink: 💼 Cloud &amp;amp; Production Systems"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The featured trio — &lt;strong&gt;Episode Atlas&lt;/strong&gt;, &lt;strong&gt;this blog&lt;/strong&gt; (Hugo on Azure), and &lt;strong&gt;Semantic Search&lt;/strong&gt; (&lt;a href="https://search.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;search.funkysi1701.com&lt;/a&gt;
) — is fully described in &lt;strong&gt;&lt;a href="#featured-projects-heading"&gt;Featured Projects&lt;/a&gt;
&lt;/strong&gt; above and not repeated here. Below is the rest of the catalogue by category.&lt;/p&gt;
&lt;h3 id="-thorne-pentecostal-church"&gt;🔧 Thorne Pentecostal Church&lt;a class="anchor ms-1" href="#-thorne-pentecostal-church" aria-label="Permalink: 🔧 Thorne Pentecostal Church"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Dependable church presence online—clear for newcomers, simple for volunteers to keep current.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.thornepentecostalchurch.co.uk" target="_blank" rel="noopener noreferrer"&gt;
&lt;img class="img-fluid" alt="Thorne Pentecostal Church website homepage" src="https://blog-dev.funkysi1701.com/images/projects/thorne-pentecostal-church.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What it does&lt;/strong&gt; — Presents service times, beliefs, and visitor information with a simple structure so members and newcomers get answers in one place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tech used&lt;/strong&gt; — HTML/CSS, Azure Static Web Apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it’s impressive&lt;/strong&gt; — Demonstrates proportionate engineering: production-grade static hosting and clear information architecture for stakeholders who need reliability, not complexity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineering signals:&lt;/strong&gt; Deployed to Azure · CI/CD via GitHub Actions&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Delivered a first web presence focused on clarity and sustainability so non-developers can keep content current with minimal process overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://www.thornepentecostalchurch.co.uk" target="_blank" rel="noopener noreferrer"&gt;Thorne Pentecostal Church&lt;/a&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-tools--utilities"&gt;🛠 Tools &amp; Utilities&lt;a class="anchor ms-1" href="#-tools--utilities" aria-label="Permalink: 🛠 Tools &amp;amp; Utilities"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="-mandelbrot-generator"&gt;🔧 Mandelbrot Generator&lt;a class="anchor ms-1" href="#-mandelbrot-generator" aria-label="Permalink: 🔧 Mandelbrot Generator"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Interactive Mandelbrot exploration in the browser—smooth zoom, pure WASM, zero server-side rendering cost.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mandelbrot.funkysi1701.com" target="_blank" rel="noopener noreferrer"&gt;
&lt;img class="img-fluid" alt="Mandelbrot Generator: fractal view in the browser" src="https://blog-dev.funkysi1701.com/images/projects/mandelbrot.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What it does&lt;/strong&gt; — Renders the Mandelbrot set in the client with zoom for desktop and mobile, demonstrating CPU-bound UI work in WASM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tech used&lt;/strong&gt; — .NET Blazor WebAssembly, Azure Static Web Apps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it’s impressive&lt;/strong&gt; — Demonstrates client-side compute discipline in Blazor WASM: rich, interactive UI with predictable cost because heavy work never hits the server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Engineering signals:&lt;/strong&gt; Deployed to Azure · Designed with clean architecture&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Outcome:&lt;/strong&gt; Built the tool to stress-test interactive Blazor WASM performance and touch UX patterns — a small codebase that still informs decisions about heavier client-side workloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://mandelbrot.funkysi1701.com" target="_blank" rel="noopener noreferrer"&gt;Mandelbrot Generator&lt;/a&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-ongoing-exploration"&gt;💡 Ongoing exploration&lt;a class="anchor ms-1" href="#-ongoing-exploration" aria-label="Permalink: 💡 Ongoing exploration"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Areas I keep hands-on with outside these shipped projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;.NET Aspire&lt;/strong&gt; — Cloud-native composition and local dev parity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt; — Orchestration and service boundaries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Elasticsearch&lt;/strong&gt; — Search and analytics stacks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Functions&lt;/strong&gt; — Event-driven and serverless patterns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevOps automation&lt;/strong&gt; — CI/CD, IaC, and release hygiene&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-collaborate-with-me"&gt;🤝 Collaborate With Me&lt;a class="anchor ms-1" href="#-collaborate-with-me" aria-label="Permalink: 🤝 Collaborate With Me"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Interested in collaborating on a project or discussing an idea?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/contact/"&gt;Contact Me&lt;/a&gt;
— Get in touch&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/funkysi1701" target="_blank" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
— Code and experiments&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.com/in/funkysi1701" target="_blank" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
— Connect professionally&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-more-resources"&gt;📚 More resources&lt;a class="anchor ms-1" href="#-more-resources" aria-label="Permalink: 📚 More resources"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/tools-and-resources/"&gt;Tools &amp;amp; Resources&lt;/a&gt;
— Recommended development tools&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/about/"&gt;About Me&lt;/a&gt;
— Background and experience&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/"&gt;Blog Posts&lt;/a&gt;
— Technical writing&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Stay visible when made redundant</title><link>https://blog-dev.funkysi1701.com/posts/2026/stay-visible-as-a-developer-when-you-are-made-redundant/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 22 Jun 2026 12:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/stay-visible-as-a-developer-when-you-are-made-redundant/</guid><category term="Career">Career</category><category term="Redundancy">Redundancy</category><category term="Networking">Networking</category><category term="Blogging">Blogging</category><category term="SideProjects">SideProjects</category><category term="Conferences">Conferences</category><category term="Developer">Developer</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/stay-visible-redundancy.png"/><description>&lt;p&gt;Sadly earlier this year I was made redundant from my Software Developer job. Looking for a new job has not been fun, but a few common themes have emerged that I wanted to share. My hope is that these will help others to put things in place that may help them in the future.&lt;/p&gt;
&lt;h2 id="go-to-events-and-meet-people"&gt;Go to events and meet people&lt;a class="anchor ms-1" href="#go-to-events-and-meet-people" aria-label="Permalink: Go to events and meet people"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Something I have said often is that I enjoy going to events or conferences, like &lt;a href="https://blog-dev.funkysi1701.com/posts/2026/ndc-london-2026"&gt;NDC London&lt;/a&gt;
. These are great places to hear interesting talks, however that is not all they are good for. When you are at a conference or user group you are in a room full of like-minded people, each of them has unique experiences of being a software developer.&lt;/p&gt;
&lt;p&gt;I am not very good at networking, I tend to stand in the corner or look awkward. However the more of these events I go to the better I get. Even if you only talk to one new person, that is one more person that you have spoken to. Want an advantage in meeting people? Volunteer. If you are a volunteer at an event, you will need to interact with other people, you may need to help direct people or assist one of the speakers. I have found this gives me a tiny bit more confidence to go and start speaking with someone or ask a question. Check out my &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/volunteering-at-ndc/"&gt;Volunteering at NDC&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;User Groups often have the same people coming week after week, take advantage of this and be one of those people as well. You will start to get noticed and remembered even if you barely speak to anyone. For me I don&amp;rsquo;t have any really local user groups, however I am enjoying spending time at &lt;a href="https://www.meetup.com/leeds-sharp/" target="_blank" rel="noopener noreferrer"&gt;LeedsSharp&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;If you genuinely had a good conversation with someone, find them on LinkedIn (or other social media) and send them a quick &amp;ldquo;It was nice chatting with you&amp;rdquo; message, this helps solidify the connection and helps you remember it.&lt;/p&gt;
&lt;p&gt;So far during this job search I have met a recruiter who I would not have met without going to events, I have had jobs suggested to me to apply for that I would not have received otherwise, and one hiring manager is someone I have met previously at an event.&lt;/p&gt;
&lt;h2 id="build-a-blog-and-write-about-what-you-are-learning"&gt;Build a blog and write about what you are learning&lt;a class="anchor ms-1" href="#build-a-blog-and-write-about-what-you-are-learning" aria-label="Permalink: Build a blog and write about what you are learning"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Almost every interview I have had has featured a comment about my blog. Now building a blog is for the long game. If you are unemployed now and don&amp;rsquo;t have a blog this won&amp;rsquo;t help you today, but if you start one and write about your experiences a few times a year it may help you tomorrow.&lt;/p&gt;
&lt;p&gt;Back in 2014 when I started blogging I wasn&amp;rsquo;t even a Software Developer, I was just an IT guy at a small company and I wanted to write about some of the cool things I was learning. I kept writing, some years I wrote more than others, there have been some big gaps where I didn&amp;rsquo;t write anything. But somehow I keep coming back to it and writing a short piece about a new version of X or a conference I have been to, or a trend that is happening.&lt;/p&gt;
&lt;p&gt;My blog is a great talking point during interviews, it helps me talk about why I think sharing information with the community is so important, what technology I have used over the years, some of the things I have learnt. My blog is featured at the top of my CV, and hiring managers have said they love it.&lt;/p&gt;
&lt;p&gt;When redundancy hit, refreshing my CV and LinkedIn was one of the first practical jobs I did: update the headline, add recent projects, link to the blog and &lt;a href="https://blog-dev.funkysi1701.com/projects"&gt;projects&lt;/a&gt;
page, and make sure nothing still implied I was in my old role. Mundane, yes — but recruiters and hiring managers check both before they ever speak to you.&lt;/p&gt;
&lt;p&gt;Today blogging isn&amp;rsquo;t the only way you can share your ideas. You could create videos, start a podcast, or have an active social media channel. I think the key is consistency, if you do a lot of it, it can easily look impressive if you look back after a few years. I personally prefer the well written blog post that I can refer back to, or search for again and again.&lt;/p&gt;
&lt;p&gt;Some ideas to get you started (but there are loads more):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What have you learnt today/this week&lt;/li&gt;
&lt;li&gt;What do you think about technology X&lt;/li&gt;
&lt;li&gt;How do you solve problem Y&lt;/li&gt;
&lt;li&gt;Compare two approaches to the same problem&lt;/li&gt;
&lt;li&gt;Your experience at a conference or meetup&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="build-side-projects"&gt;Build side projects&lt;a class="anchor ms-1" href="#build-side-projects" aria-label="Permalink: Build side projects"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Side projects are a great way to learn. You can create a side project with the goal of learning technology X, you can also blog about your journey, what problems you solved and what insights you gained from it.&lt;/p&gt;
&lt;p&gt;I personally feel there is a lot of value from getting your side project into the wild and letting users interact with it. Users will find your edge cases and ask questions you hadn&amp;rsquo;t considered. Publishing onto the internet is not free so consider your architecture carefully.&lt;/p&gt;
&lt;p&gt;What you can deploy depends on what you build. For simple websites you can do a lot with Github Pages or Azure Static Web Apps. For example this website is built with Hugo which is deployed to an Azure Static Web App for pennies per month. If you have more complex needs small Linux VMs can be created for not a lot more.&lt;/p&gt;
&lt;p&gt;I have learnt more from running a side project in production than I have the original idea. Things like DNS, SSL certificates, authentication, SEO are all very important for running a project but they would have been missed if you only ever ran your side project on localhost. I think having a rounded experience gives you a leg up when it comes to talking about your skills in an interview scenario.&lt;/p&gt;
&lt;p&gt;If you are between jobs, it is tempting to start something ambitious — the next big SaaS idea, a full-stack app with every feature you can think of. I have fallen into that trap myself. A small project you actually finish is worth more than a grand one that never ships. Pick something you can complete in a weekend or two: a CLI tool, a simple API, a static site, even a useful script. You will have something concrete to talk about in interviews, and the momentum of finishing one thing often makes the next one easier.&lt;/p&gt;
&lt;p&gt;My personal side projects can be found on my &lt;a href="https://blog-dev.funkysi1701.com/projects"&gt;projects&lt;/a&gt;
page. My inspiration came from various places, my hobbies and interests, problems I am facing or hear about, or just stuff I want to learn.&lt;/p&gt;
&lt;h2 id="keep-learning-deliberately"&gt;Keep learning deliberately&lt;a class="anchor ms-1" href="#keep-learning-deliberately" aria-label="Permalink: Keep learning deliberately"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Between applications there is often more waiting than interviewing. Use that time deliberately: look at the job descriptions you are targeting, spot a recurring skill gap, and focus on one thing rather than trying to learn everything. I listen to &lt;a href="https://blog-dev.funkysi1701.com/podcasts"&gt;development podcasts&lt;/a&gt;
on the commute and dip into courses — I have written before about using &lt;a href="https://blog-dev.funkysi1701.com/posts/2018/pluralsight/"&gt;Pluralsight&lt;/a&gt;
while doing the washing up. You will not become an expert in a fortnight, but being able to talk about what you are learning still comes up in interviews. For more general advice, see &lt;a href="https://blog-dev.funkysi1701.com/posts/2018/tips-for-developing-yourself/"&gt;Tips for Developing Yourself&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;a class="anchor ms-1" href="#closing-thoughts" aria-label="Permalink: Closing thoughts"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Being made redundant is not something I would wish on anyone, and I am still in the middle of finding my next role. Some days the job search feels slow and frustrating. On the better days, I remind myself that the things I have been doing for years — showing up at events, writing on this blog, shipping side projects, keeping my skills current — are not wasted effort. They are what give me stories to tell in interviews and reasons for people to remember my name.&lt;/p&gt;
&lt;p&gt;None of this replaces the practical side of job hunting: refreshing your CV and LinkedIn, talking to recruiters, applying properly, and working through the admin that comes with redundancy. I went through redundancy before, in 2021, and wrote about recruiters, interview rounds, and the spreadsheet I used to keep track of it all in &lt;a href="https://blog-dev.funkysi1701.com/posts/2021/lone-developer-to-senior-developer-my-2021-story/"&gt;my 2021 story&lt;/a&gt;
. Visibility helps people remember your name; applications still get you in the door.&lt;/p&gt;
&lt;p&gt;But small, consistent habits compound. You do not need to do everything at once. Pick one thing you can start this month: a user group, a blog post, a course on something job ads keep asking for, or a side project you can actually finish — &lt;a href="https://blog-dev.funkysi1701.com/start-here/"&gt;Start Here&lt;/a&gt;
collects a short reading path if you want inspiration from this blog first. Community work counts too; running a &lt;a href="https://blog-dev.funkysi1701.com/posts/2024/codeclub/"&gt;Code Club&lt;/a&gt;
taught me as much about showing up as any meetup talk. If you still have a job but worry the ground is shifting beneath you, even better — start now while you have a bit more energy and headspace.&lt;/p&gt;
&lt;p&gt;If you are going through something similar, I hope this helps. If you have found other things that worked for you, I would love to hear about them in the comments.&lt;/p&gt;
&lt;p&gt;If you have enjoyed this article and want a monthly email with my latest posts, please sign up for my &lt;a href="https://blog-dev.funkysi1701.com/newsletter"&gt;newsletter&lt;/a&gt;
. You can also &lt;a href="https://blog-dev.funkysi1701.com/contact"&gt;get in touch&lt;/a&gt;
if you have questions or just want to chat.&lt;/p&gt;</description></item><item><title>Episode Atlas: track your Trek rewatch</title><link>https://blog-dev.funkysi1701.com/posts/2026/episode-atlas/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 01 Jun 2026 12:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/episode-atlas/</guid><category term="StarTrek">StarTrek</category><category term="SideProjects">SideProjects</category><category term="EpisodeAtlas">EpisodeAtlas</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/episode-atlas/episode-atlas-home.png"/><description>&lt;p&gt;If you have ever tried to rewatch &lt;em&gt;Star Trek&lt;/em&gt; properly—not just dip into a favourite episode, but work through hundreds of instalments across multiple series—you will know the feeling. You pause &lt;em&gt;Deep Space Nine&lt;/em&gt; for a few weeks, start something else, and suddenly you have no idea which episode you were on. Spreadsheets help for a while, then they do not. Notes on your phone get lost. Memory is unreliable when you jump between TNG, DS9, and whatever new show has just dropped.&lt;/p&gt;
&lt;p&gt;That is the problem &lt;strong&gt;&lt;a href="https://www.episodeatlas.com/" target="_blank" rel="noopener noreferrer"&gt;Episode Atlas&lt;/a&gt;
&lt;/strong&gt; exists to solve: &lt;strong&gt;search 900+ episodes and films, mark what you have watched, and always know what to watch next&lt;/strong&gt;—without treating a rewatch like a project-management exercise.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Episode Atlas homepage: search and track Star Trek episodes" src="https://blog-dev.funkysi1701.com/images/2026/episode-atlas/episode-atlas-home.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/p&gt;
&lt;h2 id="why-i-built-it"&gt;Why I built it&lt;a class="anchor ms-1" href="#why-i-built-it" aria-label="Permalink: Why I built it"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I write about &lt;em&gt;Star Trek&lt;/em&gt; on this blog from time to time—episode reviews, top-50 lists, thoughts on new series. The honest origin is simpler than that, though: &lt;strong&gt;I wanted a better way to track my own rewatches&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Generic TV trackers are fine if you watch a bit of everything. They are less helpful when you care about &lt;strong&gt;one enormous franchise&lt;/strong&gt; with inconsistent stardates, films mixed in with series, and the urge to jump to &amp;ldquo;all the Borg episodes&amp;rdquo; rather than the next sequential instalment. Episode Atlas is deliberately narrow (if you can describe a database of over 900 episodes and films narrow!): all of &lt;em&gt;Star Trek&lt;/em&gt;, in one place, with features shaped by how fans actually rewatch.&lt;/p&gt;
&lt;h2 id="browse-first-sign-in-to-save"&gt;Browse first, sign in to save&lt;a class="anchor ms-1" href="#browse-first-sign-in-to-save" aria-label="Permalink: Browse first, sign in to save"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use &lt;strong&gt;&lt;a href="https://www.episodeatlas.com/" target="_blank" rel="noopener noreferrer"&gt;Episode Atlas&lt;/a&gt;
&lt;/strong&gt; without creating an account. Search the catalogue, open episodes, browse collections, and spin the random picker—no signup required.&lt;/p&gt;
&lt;p&gt;When you want your progress to stick, sign in and start your &lt;strong&gt;captain’s log&lt;/strong&gt;. Mark episodes watched, and the site remembers where you left off across every series—even if you detour from DS9 to TNG and back again.&lt;/p&gt;
&lt;h2 id="your-service-record"&gt;Your service record&lt;a class="anchor ms-1" href="#your-service-record" aria-label="Permalink: Your service record"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you are logging watches, the homepage gives you a &lt;strong&gt;service record&lt;/strong&gt; at a glance: how many of the 900+ episodes you have seen, your percentage through the franchise, and your current &lt;strong&gt;watch rank&lt;/strong&gt; (from the early ranks up through Lieutenant, Commander, and beyond). The site shows how many more episodes you need for the next rank, so a long rewatch feels like progress rather than a vague “I think I am halfway through Voyager.”&lt;/p&gt;
&lt;p&gt;You can also earn &lt;strong&gt;commendations&lt;/strong&gt; and build an &lt;strong&gt;active duty streak&lt;/strong&gt;—small nudges to keep visiting when you are in the middle of a serious rewatch. It is the same idea as a fitness streak, but for &lt;em&gt;Star Trek&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="find-any-episode-in-seconds"&gt;Find any episode in seconds&lt;a class="anchor ms-1" href="#find-any-episode-in-seconds" aria-label="Permalink: Find any episode in seconds"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Search&lt;/strong&gt; is the front door. Most visits start with “I know roughly what I want” or “which episode was that again?” Filter by series and season to narrow the list; keywords catch titles and descriptions you only half remember. Every series and the films are in one catalogue.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Episode list: find and filter Star Trek episodes" src="https://blog-dev.funkysi1701.com/images/2026/episode-atlas/episode-atlas-episodes.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Search results filtered by keyword" src="https://blog-dev.funkysi1701.com/images/2026/episode-atlas/episode-atlas-search.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/p&gt;
&lt;h2 id="episode-pages-context-and-next-steps"&gt;Episode pages: context and next steps&lt;a class="anchor ms-1" href="#episode-pages-context-and-next-steps" aria-label="Permalink: Episode pages: context and next steps"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Open any episode and you get what you need for a rewatch session: metadata, &lt;strong&gt;previous and next&lt;/strong&gt; navigation within the series, season progress, and actions to mark the episode watched or jump to the suggested &lt;strong&gt;next episode&lt;/strong&gt;. Your captain’s log ties it together—what you logged today, what you have already seen, and where to pick up when you return.&lt;/p&gt;
&lt;p&gt;The flow the site describes is simple: &lt;strong&gt;search → open → mark watched → move on&lt;/strong&gt;. Three gestures, no spreadsheet.&lt;/p&gt;
&lt;h2 id="collections-when-you-want-a-mood-not-a-season"&gt;Collections when you want a mood, not a season&lt;a class="anchor ms-1" href="#collections-when-you-want-a-mood-not-a-season" aria-label="Permalink: Collections when you want a mood, not a season"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Collections&lt;/strong&gt; answer a different question from search: &lt;em&gt;“It is late; show me something good in this mood.”&lt;/em&gt; Each collection points at Borg stories, time-travel episodes, holodeck highlights, and similar lists—fan knowledge encoded as browseable sets, so you skip filler without digging through every season.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Curated Star Trek episode collections" src="https://blog-dev.funkysi1701.com/images/2026/episode-atlas/episode-atlas-collections.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/p&gt;
&lt;h2 id="random-episode-when-you-cannot-decide"&gt;Random episode when you cannot decide&lt;a class="anchor ms-1" href="#random-episode-when-you-cannot-decide" aria-label="Permalink: Random episode when you cannot decide"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;&lt;a href="https://www.episodeatlas.com/random" target="_blank" rel="noopener noreferrer"&gt;random episode&lt;/a&gt;
&lt;/strong&gt; picker is for the evenings when you and the sofa have agreed on &lt;em&gt;Star Trek&lt;/em&gt; but not on &lt;em&gt;which&lt;/em&gt; &lt;em&gt;Star Trek&lt;/em&gt;. One click, one suggestion—useful when commitment is the hard part.&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="Random episode picker" src="https://blog-dev.funkysi1701.com/images/2026/episode-atlas/episode-atlas-random.png" loading="lazy"
width="1280" height="800"
/&gt;
&lt;/p&gt;
&lt;h2 id="how-it-compares-to-other-sites"&gt;How it compares to other sites&lt;a class="anchor ms-1" href="#how-it-compares-to-other-sites" aria-label="Permalink: How it compares to other sites"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Episode Atlas is not trying to replace &lt;a href="https://memory-alpha.fandom.com/" target="_blank" rel="noopener noreferrer"&gt;Memory Alpha&lt;/a&gt;
for lore or IMDb for cast lists. It is built for &lt;strong&gt;progress&lt;/strong&gt;: what you have watched, what rank you have earned, and what to play next. If you have ever bounced between a wiki, a ratings site, and a generic tracker and still not known where you were in DS9, this is the gap it fills.&lt;/p&gt;
&lt;h2 id="try-it"&gt;Try it&lt;a class="anchor ms-1" href="#try-it" aria-label="Permalink: Try it"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.episodeatlas.com/" target="_blank" rel="noopener noreferrer"&gt;episodeatlas.com&lt;/a&gt;
&lt;/strong&gt; — free to browse, sign in when you want your captain’s log saved. Search, mark your first episode, and see your rank climb.&lt;/p&gt;
&lt;p&gt;How do you track long rewatches—spreadsheet, app, or pure memory? If you try Episode Atlas, I would genuinely like to know what works for you and what does not: features you love, gaps that annoy you, or anything that would make a rewatch easier. Leave a comment below, or use the &lt;strong&gt;feedback&lt;/strong&gt; option on &lt;a href="https://www.episodeatlas.com/" target="_blank" rel="noopener noreferrer"&gt;episodeatlas.com&lt;/a&gt;
—both reach me, and both help shape what I build next.&lt;/p&gt;</description></item><item><title>AI on side projects: Cursor, Copilot</title><link>https://blog-dev.funkysi1701.com/posts/2026/how-i-use-ai-on-side-projects/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Fri, 10 Apr 2026 12:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/how-i-use-ai-on-side-projects/</guid><category term="AI">AI</category><category term="ChatGPT">ChatGPT</category><category term="Cursor">Cursor</category><category term="GitHubCopilot">GitHubCopilot</category><category term="SideProjects">SideProjects</category><category term="DeveloperWorkflow">DeveloperWorkflow</category><category term="Blazor">Blazor</category><category term="Hugo">Hugo</category><category term="Productivity">Productivity</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/ai-with-side-projects.png"/><description>&lt;p&gt;There is no shortage of AI tools aimed at developers right now: chat assistants, IDE completions, agents that promise to run your tests, and new products every month with overlapping features. I am not going to argue which one is &amp;ldquo;best.&amp;rdquo; Instead, here is &lt;strong&gt;what I am actually using today&lt;/strong&gt; on hobby code: &lt;a href="https://chatgpt.com/" target="_blank" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;
for quick, low-context questions, &lt;a href="https://cursor.com/" target="_blank" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;
when the work needs my repository in the loop, and &lt;a href="https://github.com/features/copilot" target="_blank" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;
for fast inline help while I type. That trio might change, but it reflects how I have learned to spend money and attention in 2026.&lt;/p&gt;
&lt;p&gt;The through-line is simple: &lt;strong&gt;match the tool to how much context the problem needs&lt;/strong&gt;. That stops me from dumping half a repo into a browser tab for a vague design question, or firing up an editor assistant when I only wanted a two-paragraph explanation of something I could read in the docs.&lt;/p&gt;
&lt;p&gt;This is not a product review. It is a snapshot of how I work, using the projects on &lt;a href="https://blog-dev.funkysi1701.com/projects/"&gt;my projects page&lt;/a&gt;
as concrete examples.&lt;/p&gt;
&lt;h2 id="chatgpt-questions-that-do-not-need-my-codebase"&gt;ChatGPT: questions that do not need my codebase&lt;a class="anchor ms-1" href="#chatgpt-questions-that-do-not-need-my-codebase" aria-label="Permalink: ChatGPT: questions that do not need my codebase"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://chatgpt.com/" target="_blank" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;
is still my first stop when I am trying to &lt;em&gt;frame&lt;/em&gt; a problem. I use it when the answer is mostly portable knowledge: comparisons, vocabulary, &amp;ldquo;what should I read next,&amp;rdquo; and sanity checks that do not depend on the exact shape of my solution.&lt;/p&gt;
&lt;p&gt;A few examples from things I have built:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;This blog (Hugo + Azure Static Web Apps)&lt;/strong&gt; — I might ask how people usually implement site search on static sites, or what the trade-offs are between generating a JSON index at build time versus leaning on a hosted search service. I am not looking for a drop-in patch to my theme; I want a short list of options so I can decide what fits a hobby budget.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.episodeatlas.com/" target="_blank" rel="noopener noreferrer"&gt;Episode Atlas&lt;/a&gt;
(Blazor, Functions, Cosmos DB)&lt;/strong&gt; — good prompts sound like &amp;ldquo;patterns for tracking per-user progress through a large catalogue&amp;rdquo; or &amp;ldquo;when a tiny Blazor app might still justify a separate API layer.&amp;rdquo; Those are design conversations. The model does not need to see my episode containers to help me think about partition keys and stale data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://mandelbrot.funkysi1701.com/" target="_blank" rel="noopener noreferrer"&gt;Mandelbrot Generator&lt;/a&gt;
(Blazor WebAssembly)&lt;/strong&gt; — I have used general chats to reason about why browser-side fractals feel CPU-bound, or what people typically do first when zoom performance tanks. Again, that is physics-of-the-platform stuff, not &amp;ldquo;line 40 of my render loop.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Experiments&lt;/strong&gt; — when I am poking at &lt;a href="https://blog-dev.funkysi1701.com/projects/"&gt;.NET Aspire&lt;/a&gt;
, Kubernetes, Elasticsearch, or Azure Functions in my own time, ChatGPT is useful for learning paths: what to skim first, what terms I am mixing up, and what I am over-engineering for a toy.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Where ChatGPT falls down is predictable: anything that requires &lt;em&gt;my&lt;/em&gt; wiring—pipeline YAML that only fails in CI, a Hugo partial that interacts with three others, a Blazor component that only breaks after publish. For that, context wins.&lt;/p&gt;
&lt;h2 id="cursor-when-the-answer-is-in-the-repo"&gt;Cursor: when the answer is in the repo&lt;a class="anchor ms-1" href="#cursor-when-the-answer-is-in-the-repo" aria-label="Permalink: Cursor: when the answer is in the repo"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://cursor.com/" target="_blank" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;
(or any &amp;ldquo;chat with your codebase&amp;rdquo; style editor) is where I go when I need &lt;strong&gt;consistency across files&lt;/strong&gt; and &lt;strong&gt;diff-shaped output&lt;/strong&gt;. Side projects accumulate small decisions—naming, folder layout, how strict nullability is—and the model only respects those if it can see them.&lt;/p&gt;
&lt;p&gt;Typical jobs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Blog platform&lt;/strong&gt; — adjusting a Hugo layout or partial, fixing warnings after a Hugo upgrade, or tweaking GitHub Actions / Azure Pipelines so the build still emits RSS and the JSON search index. Those changes are boring, fiddly, and easy to get subtly wrong if you guess from memory.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Episode Atlas&lt;/strong&gt; — refactoring a Blazor page, aligning a Function endpoint with what the client expects, or tightening a Cosmos query once I know the access pattern. Here I want suggestions that match &lt;em&gt;my&lt;/em&gt; project structure, not a generic sample from the internet.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mandelbrot&lt;/strong&gt; — iterating on rendering code where small mistakes show up as wrong colours or runaway iterations. Having the file in context beats describing the algorithm in chat.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.thornepentecostalchurch.co.uk/" target="_blank" rel="noopener noreferrer"&gt;Thorne Pentecostal Church&lt;/a&gt;
site&lt;/strong&gt; — simple HTML and CSS still benefit from repo context when the goal is &amp;ldquo;match what is already there&amp;rdquo; rather than &amp;ldquo;invent a new design system.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I still treat Cursor as an accelerator, not an author. I read diffs, run the site or tests, and verify anything touching hosting or secrets. Side projects are a great place to learn &lt;em&gt;when&lt;/em&gt; to trust an edit—and when the model confidently invents an API that never existed.&lt;/p&gt;
&lt;h2 id="github-copilot-the-inline-teammate"&gt;GitHub Copilot: the inline teammate&lt;a class="anchor ms-1" href="#github-copilot-the-inline-teammate" aria-label="Permalink: GitHub Copilot: the inline teammate"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/features/copilot" target="_blank" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;
fills a different gap. Where Cursor often drives &lt;strong&gt;multi-file&lt;/strong&gt; or &lt;strong&gt;exploratory&lt;/strong&gt; edits, Copilot shines on &lt;strong&gt;local&lt;/strong&gt; work: finishing a line, sketching a test, or repeating a pattern I have already established in the file.&lt;/p&gt;
&lt;p&gt;I reach for it when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I am writing repetitive Blazor markup or boilerplate DTOs.&lt;/li&gt;
&lt;li&gt;I am editing YAML or shell and want a plausible next stanza that matches the block above.&lt;/li&gt;
&lt;li&gt;I already know what I am doing and want speed more than architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is overlap between Copilot and Cursor in my workflow, and I am fine with that. If you only pay for one tool, you can still apply the same &lt;em&gt;principle&lt;/em&gt;: shallow context versus deep context.&lt;/p&gt;
&lt;h2 id="a-quick-decision-table"&gt;A quick decision table&lt;a class="anchor ms-1" href="#a-quick-decision-table" aria-label="Permalink: A quick decision table"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;What I use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ldquo;What are my options?&amp;rdquo; / compare approaches&lt;/td&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ldquo;Change this project to do X&amp;rdquo;&lt;/td&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ldquo;Finish this method / this pipeline step&amp;rdquo;&lt;/td&gt;
&lt;td&gt;Copilot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security, secrets, production incidents&lt;/td&gt;
&lt;td&gt;Me, plus docs—AI optional and heavily verified&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="boundaries-privacy-and-verification"&gt;Boundaries, privacy, and verification&lt;a class="anchor ms-1" href="#boundaries-privacy-and-verification" aria-label="Permalink: Boundaries, privacy, and verification"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What I do not put in AI tools.&lt;/strong&gt; I treat anything that could identify a person, anything contractual from work, and anything that unlocks production (passwords, API keys, connection strings, private certificates) as off-limits unless the product’s policy and my employer’s policy both say otherwise. For side projects that usually means personal repos only, secrets in environment variables or Key Vault—not in chat—and if I need help with a failing pipeline I redact resource names and paste error text, not the whole variable block. If you have a day job, your company’s acceptable-use rules beat anything you read in a blog post; when in doubt, use vendor docs and internal support channels instead of a model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How I check output before it counts.&lt;/strong&gt; I never merge a multi-file change on trust alone: I read the diff file by file, run &lt;code&gt;hugo&lt;/code&gt; / &lt;code&gt;dotnet build&lt;/code&gt; / whatever the project’s normal build is, and exercise the path I changed in the browser or with a quick manual test. If the assistant suggested a library call or HTTP API I do not recognise, I cross-check the official documentation or source before I rely on it—side projects are where I have been burned by plausible-but-wrong method names. For anything security-sensitive (auth callbacks, CORS, SQL, dependency upgrades), I assume the model is giving me a sketch and I still apply the same review I would give a junior’s pull request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning paths are still verified.&lt;/strong&gt; When I use ChatGPT to orient around Aspire, Kubernetes, or Elasticsearch, I use it to build a mental map and a reading list, then I confirm version-specific behaviour in Microsoft Learn, Kubernetes docs, or Elastic’s docs. The goal is to shorten &lt;em&gt;where to look&lt;/em&gt;, not to skip looking.&lt;/p&gt;
&lt;h2 id="closing-thought"&gt;Closing thought&lt;a class="anchor ms-1" href="#closing-thought" aria-label="Permalink: Closing thought"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;AI tools have not replaced the fun of side projects for me. Projects like Episode Atlas exist because I wanted a better way to track &lt;em&gt;Star Trek&lt;/em&gt; rewatches across hundreds of episodes; the Mandelbrot app exists because fractals are still cool; this blog exists because writing things down forces clarity. What has changed is how fast I can get through the glue work—if I pick the right assistant for the amount of context the task really needs.&lt;/p&gt;
&lt;p&gt;If you are a developer experimenting with the same stack—.NET, static sites, Azure—how do you split tools today? I would be interested to hear what works for you in the comments.&lt;/p&gt;</description></item><item><title>AI will not replace us developers</title><link>https://blog-dev.funkysi1701.com/posts/2026/ai-wont-replace-developers/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Sat, 14 Mar 2026 09:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/ai-wont-replace-developers/</guid><category term="AI">AI</category><category term="SoftwareDevelopment">SoftwareDevelopment</category><category term="Career">Career</category><category term="engineering">engineering</category><category term="Productivity">Productivity</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/ai-wont-replace-developers.png"/><description>&lt;h2 id="introduction--the-shift-we-are-living-through"&gt;Introduction – The Shift We are Living Through&lt;a class="anchor ms-1" href="#introduction--the-shift-we-are-living-through" aria-label="Permalink: Introduction – The Shift We are Living Through"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I write this, I have just been made redundant. AI is not being used to explain this particular redundancy, but it is being used to explain many redundancies across the industry.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;I just got laid off..... Again...... I am still in shock. I loved my new job and was creating great impact but here we are again. Layoffs are not personal so I am ok but yeh will be looking for something new...... Again......&lt;/p&gt;&amp;mdash; Debbie O&amp;#39;Brien (@debs_obrien) &lt;a href="https://twitter.com/debs_obrien/status/2027171846743892254?ref_src=twsrc%5Etfw"&gt;February 27, 2026&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;I listened to an excellent talk at &lt;a href="https://blog-dev.funkysi1701.com/posts/2026/ndc-london-2026/"&gt;NDC London&lt;/a&gt;
by Debbie O&amp;rsquo;Brien about using AI to do amazing things with &lt;a href="https://youtu.be/Numb52aJkJw" target="_blank" rel="noopener noreferrer"&gt;Playwright MCP&lt;/a&gt;
. If people who are using AI to its fullest are losing their jobs to AI, what hope is there for our industry? Are we all going to be writing AI prompts and that&amp;rsquo;s it?&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s stop for a minute. Software development is an ever-changing industry. There is always something new to learn and this AI craze is no different.&lt;/p&gt;
&lt;p&gt;The role of the developer has always shifted as tools improved. We moved from writing assembly to high-level languages, from manual deployments to automated pipelines, and from managing servers to deploying infrastructure with a few commands.&lt;/p&gt;
&lt;p&gt;The real question is how our role is changing and how we need to adapt and make the most of the new tools available to us.&lt;/p&gt;
&lt;h2 id="whats-actually-changed"&gt;What’s Actually Changed?&lt;a class="anchor ms-1" href="#whats-actually-changed" aria-label="Permalink: What’s Actually Changed?"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a lot of noise around AI in software development. Headlines swing between &amp;ldquo;AI will replace developers&amp;rdquo; and &amp;ldquo;AI is just autocomplete.&amp;rdquo; The truth, as usual, sits somewhere in the middle.&lt;/p&gt;
&lt;p&gt;But something real has changed. Not just the tools we use, but the speed, shape, and workflow of software development.&lt;/p&gt;
&lt;h3 id="from-reactive-to-proactive"&gt;From Reactive to Proactive&lt;a class="anchor ms-1" href="#from-reactive-to-proactive" aria-label="Permalink: From Reactive to Proactive"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For years, developer productivity tools focused on helping us type faster.&lt;/p&gt;
&lt;p&gt;We had IntelliSense, snippets, code generation tools, and IDE refactorings. These were useful, but they were fundamentally reactive. They waited for the developer to start typing and then offered suggestions.&lt;/p&gt;
&lt;p&gt;AI coding tools have crossed a different threshold.&lt;/p&gt;
&lt;p&gt;Instead of simply completing the next line, they can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generate entire classes or modules&lt;/li&gt;
&lt;li&gt;Suggest architecture patterns&lt;/li&gt;
&lt;li&gt;Write tests&lt;/li&gt;
&lt;li&gt;Perform multi-file refactors&lt;/li&gt;
&lt;li&gt;Explain unfamiliar codebases&lt;/li&gt;
&lt;li&gt;Propose fixes for bugs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The important shift is this:&lt;/p&gt;
&lt;p&gt;The tool is no longer just responding to the code — it&amp;rsquo;s responding to the intent.&lt;/p&gt;
&lt;p&gt;You describe a goal, and the AI attempts to produce the implementation.&lt;/p&gt;
&lt;p&gt;This moves AI from being an autocomplete engine to something closer to an execution partner.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not autonomous in the sense of replacing developers, but it does change the relationship between the developer and the code.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re no longer writing every line ourselves. Increasingly, we are directing the creation of code rather than manually constructing it.&lt;/p&gt;
&lt;h3 id="the-cost-of-a-first-draft-has-collapsed"&gt;The Cost of a First Draft Has Collapsed&lt;a class="anchor ms-1" href="#the-cost-of-a-first-draft-has-collapsed" aria-label="Permalink: The Cost of a First Draft Has Collapsed"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Historically, one of the hidden costs of software development was simply getting started.&lt;/p&gt;
&lt;p&gt;Creating a prototype involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting up project structure&lt;/li&gt;
&lt;li&gt;Writing boilerplate&lt;/li&gt;
&lt;li&gt;Defining models&lt;/li&gt;
&lt;li&gt;Creating basic tests&lt;/li&gt;
&lt;li&gt;Wiring components together&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this work was intellectually difficult, but it took time.&lt;/p&gt;
&lt;p&gt;AI tools have dramatically reduced the cost of that first draft.&lt;/p&gt;
&lt;p&gt;You can now describe a feature and quickly get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A rough implementation&lt;/li&gt;
&lt;li&gt;Basic unit tests&lt;/li&gt;
&lt;li&gt;A starting architecture&lt;/li&gt;
&lt;li&gt;A prototype that actually runs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This doesn&amp;rsquo;t mean the output is perfect. In fact, it often isn&amp;rsquo;t. But the barrier to experimentation has dropped significantly.&lt;/p&gt;
&lt;p&gt;Ideas that might have taken half a day to prototype can now be explored in minutes.&lt;/p&gt;
&lt;p&gt;And that has a subtle but powerful effect on development culture:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When experimentation becomes cheap, developers try more ideas.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This encourages iteration, exploration, and rapid feedback in ways that previously required more discipline and effort.&lt;/p&gt;
&lt;h3 id="writing-code-is-no-longer-the-bottleneck"&gt;Writing Code Is No Longer the Bottleneck&lt;a class="anchor ms-1" href="#writing-code-is-no-longer-the-bottleneck" aria-label="Permalink: Writing Code Is No Longer the Bottleneck"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the most surprising outcomes of AI coding tools is that they reveal something many experienced developers already suspected:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Typing code was never the hardest part of software development.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The real difficulty lies elsewhere:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understanding the problem&lt;/li&gt;
&lt;li&gt;Designing a clean solution&lt;/li&gt;
&lt;li&gt;Managing complexity&lt;/li&gt;
&lt;li&gt;Communicating intent to other developers&lt;/li&gt;
&lt;li&gt;Maintaining systems over time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI can produce large volumes of code very quickly. But that doesn&amp;rsquo;t automatically translate into good software.&lt;/p&gt;
&lt;p&gt;If anything, it shifts the constraint.&lt;/p&gt;
&lt;p&gt;The bottleneck is no longer how fast code can be written.&lt;/p&gt;
&lt;p&gt;The bottleneck is now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How clearly can problems be defined&lt;/li&gt;
&lt;li&gt;How well can systems be designed&lt;/li&gt;
&lt;li&gt;How effectively can code be reviewed and maintained&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, AI accelerates the mechanical part of development, but the thinking part becomes even more important.&lt;/p&gt;
&lt;h2 id="what-hasnt-changed-but-people-think-it-has"&gt;What Hasn’t Changed (But People Think It Has)&lt;a class="anchor ms-1" href="#what-hasnt-changed-but-people-think-it-has" aria-label="Permalink: What Hasn’t Changed (But People Think It Has)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With every major technological shift, there&amp;rsquo;s a temptation to assume everything is different now. AI has amplified that feeling in software development. When tools can generate large amounts of code in seconds, it&amp;rsquo;s easy to believe the fundamentals of the profession have been rewritten.&lt;/p&gt;
&lt;p&gt;But when you look closely at real software projects, something interesting appears: many of the hardest parts of software development haven&amp;rsquo;t changed at all.&lt;/p&gt;
&lt;p&gt;AI has accelerated the mechanics of coding, but the underlying challenges of building reliable, useful software are still very much human problems.&lt;/p&gt;
&lt;h3 id="understanding-requirements-is-still-the-hardest-problem"&gt;Understanding Requirements Is Still the Hardest Problem&lt;a class="anchor ms-1" href="#understanding-requirements-is-still-the-hardest-problem" aria-label="Permalink: Understanding Requirements Is Still the Hardest Problem"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Software projects rarely fail because developers can&amp;rsquo;t write code. They fail because the problem wasn&amp;rsquo;t clearly understood in the first place.&lt;/p&gt;
&lt;p&gt;AI tools can generate implementations, but they rely entirely on the input they&amp;rsquo;re given. If the requirement is vague, incomplete, or contradictory, the output will reflect that.&lt;/p&gt;
&lt;p&gt;Consider how requirements often arrive in real projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A stakeholder describes a problem in business language.&lt;/li&gt;
&lt;li&gt;Important edge cases are discovered later.&lt;/li&gt;
&lt;li&gt;Different teams have slightly different expectations.&lt;/li&gt;
&lt;li&gt;Requirements evolve as users interact with the system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this has changed with AI.&lt;/p&gt;
&lt;p&gt;The ability to ask the right questions, clarify ambiguity, and translate business needs into technical solutions remains one of the most valuable skills a developer can have.&lt;/p&gt;
&lt;p&gt;In fact, as code generation becomes easier, clear problem definition becomes even more important.&lt;/p&gt;
&lt;h3 id="architecture-and-trade-offs-still-matter"&gt;Architecture and Trade-Offs Still Matter&lt;a class="anchor ms-1" href="#architecture-and-trade-offs-still-matter" aria-label="Permalink: Architecture and Trade-Offs Still Matter"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;AI can suggest design patterns, propose architectures, and generate project structures. But good software architecture isn&amp;rsquo;t just about picking the right pattern.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s about making trade-offs.&lt;/p&gt;
&lt;p&gt;Questions like these still require human judgment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Should this be a microservice or part of the monolith?&lt;/li&gt;
&lt;li&gt;Is the added complexity of caching worth the performance gain?&lt;/li&gt;
&lt;li&gt;What level of abstraction will make this system maintainable in five years?&lt;/li&gt;
&lt;li&gt;Where should the boundaries between components sit?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These decisions depend heavily on context:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Team size&lt;/li&gt;
&lt;li&gt;Deployment environment&lt;/li&gt;
&lt;li&gt;Operational constraints&lt;/li&gt;
&lt;li&gt;Long-term maintenance costs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI can offer suggestions, but it cannot fully understand the organisational and operational realities of the system it&amp;rsquo;s helping to build.&lt;/p&gt;
&lt;p&gt;That responsibility still sits with the developer.&lt;/p&gt;
&lt;h3 id="accountability-doesnt-disappear"&gt;Accountability Doesn’t Disappear&lt;a class="anchor ms-1" href="#accountability-doesnt-disappear" aria-label="Permalink: Accountability Doesn’t Disappear"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One thing that definitely hasn&amp;rsquo;t changed is ownership.&lt;/p&gt;
&lt;p&gt;When software breaks in production, nobody blames the IDE. The same principle applies to AI.&lt;/p&gt;
&lt;p&gt;If an AI-generated query deletes the wrong data, or a generated implementation introduces a subtle performance issue, the responsibility still lies with the developer who reviewed and shipped the code.&lt;/p&gt;
&lt;p&gt;Production systems still require:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Careful testing&lt;/li&gt;
&lt;li&gt;Monitoring and observability&lt;/li&gt;
&lt;li&gt;Security awareness&lt;/li&gt;
&lt;li&gt;Performance tuning&lt;/li&gt;
&lt;li&gt;Ongoing maintenance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI can assist with many of these areas, but it doesn&amp;rsquo;t remove the need for human accountability.&lt;/p&gt;
&lt;p&gt;At the end of the day, someone has to understand the system well enough to take responsibility for it.&lt;/p&gt;
&lt;p&gt;And that hasn&amp;rsquo;t changed at all.&lt;/p&gt;
&lt;h2 id="the-new-developer-skill-stack"&gt;The New Developer Skill Stack&lt;a class="anchor ms-1" href="#the-new-developer-skill-stack" aria-label="Permalink: The New Developer Skill Stack"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If we accept that AI has collapsed the cost of a first draft, then the skill stack shifts upward.&lt;/p&gt;
&lt;p&gt;Not because the old skills are irrelevant, but because they&amp;rsquo;re no longer the limiting factor as often.&lt;/p&gt;
&lt;p&gt;Syntax still matters. Fundamentals still matter. But the competitive advantage moves towards the parts of the job that are hard to automate: precision, judgment, and system-level thinking.&lt;/p&gt;
&lt;p&gt;Think of it as an evolution of the same craft.&lt;/p&gt;
&lt;h3 id="prompting-as-a-technical-skill"&gt;Prompting as a Technical Skill&lt;a class="anchor ms-1" href="#prompting-as-a-technical-skill" aria-label="Permalink: Prompting as a Technical Skill"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;Prompting&amp;rdquo; sounds fluffy until you realise what it actually is in practice: specifying a problem clearly enough that another agent (human or machine) can make progress without guessing.&lt;/p&gt;
&lt;p&gt;The better you are at being precise, the more useful the tool becomes.&lt;/p&gt;
&lt;p&gt;That means getting specific about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The goal (what &amp;ldquo;done&amp;rdquo; looks like)&lt;/li&gt;
&lt;li&gt;The constraints (performance, security, backward compatibility, tech stack)&lt;/li&gt;
&lt;li&gt;Inputs/outputs and edge cases&lt;/li&gt;
&lt;li&gt;The environment (language version, framework, database, deployment model)&lt;/li&gt;
&lt;li&gt;The trade-offs you care about (simplicity vs flexibility, latency vs cost)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you ask an AI to &amp;ldquo;build a feature&amp;rdquo;, you&amp;rsquo;ll get a lot of code, but it will almost always bake in assumptions you didn&amp;rsquo;t mean to make.&lt;/p&gt;
&lt;p&gt;The best results come when you treat it like a junior developer with infinite typing speed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask it to propose an approach first&lt;/li&gt;
&lt;li&gt;Get it to list assumptions and risks&lt;/li&gt;
&lt;li&gt;Then implement one slice at a time (and keep the changes small)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, iterative refinement becomes the workflow.&lt;/p&gt;
&lt;p&gt;You rarely get the best output on the first prompt. But you can converge quickly if you review the result, point out what is wrong or missing, and re-run with tighter guidance.&lt;/p&gt;
&lt;p&gt;In other words, prompting is less about &amp;ldquo;magic words&amp;rdquo; and more about feedback loops.&lt;/p&gt;
&lt;h3 id="code-review-becomes-critical-thinking"&gt;Code Review Becomes Critical Thinking&lt;a class="anchor ms-1" href="#code-review-becomes-critical-thinking" aria-label="Permalink: Code Review Becomes Critical Thinking"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When AI can generate 200 lines of code in seconds, reviewing becomes the high-leverage skill.&lt;/p&gt;
&lt;p&gt;That review can&amp;rsquo;t just be &amp;ldquo;does it compile&amp;rdquo; or &amp;ldquo;does it look idiomatic&amp;rdquo;. It has to be critical thinking.&lt;/p&gt;
&lt;p&gt;AI-generated code often fails in ways that are subtle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It handles the happy path beautifully and ignores weird real-world inputs&lt;/li&gt;
&lt;li&gt;It silently changes behaviour (especially around dates, nulls, and defaults)&lt;/li&gt;
&lt;li&gt;It introduces concurrency bugs that only show up under load&lt;/li&gt;
&lt;li&gt;It uses a library/API incorrectly but convincingly&lt;/li&gt;
&lt;li&gt;It passes tests that are too shallow to be meaningful&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most important thing to review is not the code itself, but the assumptions underneath it.&lt;/p&gt;
&lt;p&gt;I find it useful to explicitly ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What does this code assume about the data?&lt;/li&gt;
&lt;li&gt;What does it assume about ordering, timing, and retries?&lt;/li&gt;
&lt;li&gt;What happens when dependencies fail?&lt;/li&gt;
&lt;li&gt;What would I see in logs/metrics if this breaks in production?&lt;/li&gt;
&lt;li&gt;What tests would convince me this is correct?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where experienced developers become even more valuable: not because they can type faster, but because they&amp;rsquo;ve seen how systems fail.&lt;/p&gt;
&lt;h3 id="systems-thinking--syntax-knowledge"&gt;Systems Thinking &gt; Syntax Knowledge&lt;a class="anchor ms-1" href="#systems-thinking--syntax-knowledge" aria-label="Permalink: Systems Thinking &amp;gt; Syntax Knowledge"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;AI is good at syntax and local patterns. It is much worse at understanding the bigger picture.&lt;/p&gt;
&lt;p&gt;The developers who thrive in this shift will be the ones who can hold the system in their head:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How data flows through the application&lt;/li&gt;
&lt;li&gt;Where the boundaries are between components&lt;/li&gt;
&lt;li&gt;What needs to be observable in production (logs, metrics, traces)&lt;/li&gt;
&lt;li&gt;What makes the codebase maintainable for the next person&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Systems thinking is what stops an AI-generated &amp;ldquo;solution&amp;rdquo; from becoming a future incident.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also why the old advice from &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/pragmatic-programmer/"&gt;The Pragmatic Programmer&lt;/a&gt;
matters even more now: remember the big picture.&lt;/p&gt;
&lt;p&gt;Not &amp;ldquo;can we generate this function&amp;rdquo;, but:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is this the simplest thing that could work?&lt;/li&gt;
&lt;li&gt;Can we operate it at 2am?&lt;/li&gt;
&lt;li&gt;Can we change it safely in six months?&lt;/li&gt;
&lt;li&gt;Are we making the system easier to understand or harder?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI accelerates output, but it doesn&amp;rsquo;t automatically improve outcomes, and that creates real risk if we aren&amp;rsquo;t careful.&lt;/p&gt;
&lt;h2 id="the-risk-shallow-engineers"&gt;The Risk: Shallow Engineers&lt;a class="anchor ms-1" href="#the-risk-shallow-engineers" aria-label="Permalink: The Risk: Shallow Engineers"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The biggest danger is not that AI replaces us.&lt;/p&gt;
&lt;p&gt;The biggest danger is that it makes us lazy.&lt;/p&gt;
&lt;p&gt;If we outsource too much thinking to tools, we can accidentally create a generation of engineers who can ship quickly but cannot explain what they shipped, why it works, or how to fix it when it fails.&lt;/p&gt;
&lt;p&gt;That risk shows up in subtle ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Copying generated code without understanding the trade-offs&lt;/li&gt;
&lt;li&gt;Accepting tests because they are green, even when they don&amp;rsquo;t test anything meaningful&lt;/li&gt;
&lt;li&gt;Mistaking confidence in the AI&amp;rsquo;s tone for correctness in the implementation&lt;/li&gt;
&lt;li&gt;Losing touch with fundamentals like debugging, data structures, networking, and performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is especially dangerous under delivery pressure. If a team rewards speed above all else, AI can amplify the wrong behaviour: lots of output, little understanding.&lt;/p&gt;
&lt;p&gt;And that debt compounds.&lt;/p&gt;
&lt;p&gt;Six months later, nobody trusts the codebase, incidents increase, and every change becomes risky.&lt;/p&gt;
&lt;p&gt;So yes, AI can make us faster. But if we use it without discipline, it can also make us shallow.&lt;/p&gt;
&lt;p&gt;The antidote is simple, even if not always easy: use AI to accelerate execution, not to bypass understanding.&lt;/p&gt;
&lt;h2 id="the-opportunity-increased-leverage-for-the-right-people"&gt;The Opportunity: Increased Leverage (For the Right People)&lt;a class="anchor ms-1" href="#the-opportunity-increased-leverage-for-the-right-people" aria-label="Permalink: The Opportunity: Increased Leverage (For the Right People)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The upside is huge for developers who combine AI with strong engineering habits.&lt;/p&gt;
&lt;p&gt;When you can generate a solid first draft in minutes, you can spend more time where value actually lives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better problem framing&lt;/li&gt;
&lt;li&gt;Better architecture decisions&lt;/li&gt;
&lt;li&gt;Better test strategy&lt;/li&gt;
&lt;li&gt;Better operational design&lt;/li&gt;
&lt;li&gt;Better developer experience for your team&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where leverage appears.&lt;/p&gt;
&lt;p&gt;One thoughtful developer with AI support can now do the kind of end-to-end work that used to require more hand-offs and more waiting.&lt;/p&gt;
&lt;p&gt;Not because they became magically smarter overnight, but because the mechanical drag has been reduced.&lt;/p&gt;
&lt;p&gt;If your fundamentals are strong, AI multiplies your impact.&lt;/p&gt;
&lt;p&gt;If your fundamentals are weak, AI multiplies your mistakes.&lt;/p&gt;
&lt;p&gt;That is the real divide.&lt;/p&gt;
&lt;h2 id="practical-advice-for-developers"&gt;Practical Advice for Developers&lt;a class="anchor ms-1" href="#practical-advice-for-developers" aria-label="Permalink: Practical Advice for Developers"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to stay relevant and thrive in this shift, here is what I would do right now.&lt;/p&gt;
&lt;h3 id="1-treat-ai-output-as-a-draft-not-truth"&gt;1. Treat AI Output as a Draft, Not Truth&lt;a class="anchor ms-1" href="#1-treat-ai-output-as-a-draft-not-truth" aria-label="Permalink: 1. Treat AI Output as a Draft, Not Truth"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Assume every generated answer is a proposal.&lt;/p&gt;
&lt;p&gt;Read it. Challenge it. Test it.&lt;/p&gt;
&lt;p&gt;Never merge code you cannot explain to another developer.&lt;/p&gt;
&lt;h3 id="2-strengthen-your-core-engineering-muscles"&gt;2. Strengthen Your Core Engineering Muscles&lt;a class="anchor ms-1" href="#2-strengthen-your-core-engineering-muscles" aria-label="Permalink: 2. Strengthen Your Core Engineering Muscles"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Double down on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Testing strategy&lt;/li&gt;
&lt;li&gt;System design&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Performance and security fundamentals&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are durable skills and AI makes them more valuable, not less.&lt;/p&gt;
&lt;h3 id="3-get-better-at-problem-framing"&gt;3. Get Better at Problem Framing&lt;a class="anchor ms-1" href="#3-get-better-at-problem-framing" aria-label="Permalink: 3. Get Better at Problem Framing"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before asking AI to implement anything, write down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The problem in one sentence&lt;/li&gt;
&lt;li&gt;The constraints&lt;/li&gt;
&lt;li&gt;The non-goals&lt;/li&gt;
&lt;li&gt;The acceptance criteria&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This habit improves both the AI output and your own clarity.&lt;/p&gt;
&lt;h3 id="4-work-in-small-reviewable-slices"&gt;4. Work in Small, Reviewable Slices&lt;a class="anchor ms-1" href="#4-work-in-small-reviewable-slices" aria-label="Permalink: 4. Work in Small, Reviewable Slices"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ask AI for smaller changes, not giant rewrites.&lt;/p&gt;
&lt;p&gt;Small slices make it easier to reason about behaviour, review quality, and rollback safely.&lt;/p&gt;
&lt;h3 id="5-raise-the-bar-for-code-reviews"&gt;5. Raise the Bar for Code Reviews&lt;a class="anchor ms-1" href="#5-raise-the-bar-for-code-reviews" aria-label="Permalink: 5. Raise the Bar for Code Reviews"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Review assumptions, failure modes, and operational impact, not just style.&lt;/p&gt;
&lt;p&gt;A useful question in every review: &amp;ldquo;What breaks first in production?&amp;rdquo;&lt;/p&gt;
&lt;h3 id="6-build-a-personal-ai-workflow"&gt;6. Build a Personal AI Workflow&lt;a class="anchor ms-1" href="#6-build-a-personal-ai-workflow" aria-label="Permalink: 6. Build a Personal AI Workflow"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create repeatable prompt patterns for tasks you do often:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing tests&lt;/li&gt;
&lt;li&gt;Refactoring legacy code&lt;/li&gt;
&lt;li&gt;Generating migration scripts&lt;/li&gt;
&lt;li&gt;Creating docs from code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Treat these as reusable tools in your engineering toolbox.&lt;/p&gt;
&lt;h3 id="7-keep-shipping-real-things"&gt;7. Keep Shipping Real Things&lt;a class="anchor ms-1" href="#7-keep-shipping-real-things" aria-label="Permalink: 7. Keep Shipping Real Things"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The best learning still comes from production reality.&lt;/p&gt;
&lt;p&gt;Build, deploy, monitor, and improve. Feedback from real users teaches lessons no model can replace.&lt;/p&gt;
&lt;h2 id="what-this-means-for-juniors-vs-seniors"&gt;What This Means for Juniors vs Seniors&lt;a class="anchor ms-1" href="#what-this-means-for-juniors-vs-seniors" aria-label="Permalink: What This Means for Juniors vs Seniors"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;AI changes the game for everyone, but not in exactly the same way.&lt;/p&gt;
&lt;p&gt;For junior developers, AI can be a force multiplier for learning if used correctly.&lt;/p&gt;
&lt;p&gt;It can explain unfamiliar concepts, generate examples, and speed up experimentation. But juniors still need mentorship and deliberate practice to build judgment.&lt;/p&gt;
&lt;p&gt;If you skip the fundamentals and rely only on generated output, you&amp;rsquo;ll progress fast on paper and slowly in reality.&lt;/p&gt;
&lt;p&gt;For senior developers, the expectation shifts from individual output to system-level impact.&lt;/p&gt;
&lt;p&gt;Seniors become the multipliers of multipliers: setting standards, designing guardrails, mentoring teams, and ensuring AI usage improves quality rather than eroding it.&lt;/p&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Juniors should use AI to learn faster&lt;/li&gt;
&lt;li&gt;Seniors should use AI to scale better decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both groups win when curiosity stays high and ego stays low.&lt;/p&gt;
&lt;h2 id="the-big-question-are-we-becoming-directors-instead-of-builders"&gt;The Big Question: Are We Becoming Directors Instead of Builders?&lt;a class="anchor ms-1" href="#the-big-question-are-we-becoming-directors-instead-of-builders" aria-label="Permalink: The Big Question: Are We Becoming Directors Instead of Builders?"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Partly, yes, and that is not a bad thing.&lt;/p&gt;
&lt;p&gt;More of our time is moving toward direction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Defining intent&lt;/li&gt;
&lt;li&gt;Making trade-offs&lt;/li&gt;
&lt;li&gt;Reviewing outputs&lt;/li&gt;
&lt;li&gt;Orchestrating systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But we are still builders.&lt;/p&gt;
&lt;p&gt;Good directors in software are grounded in reality. They understand implementation details, constraints, and failure modes. They can still get into the code when needed.&lt;/p&gt;
&lt;p&gt;So the role is not &amp;ldquo;director instead of builder.&amp;rdquo; It is &amp;ldquo;builder with broader leverage.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We are not giving up craftsmanship.&lt;/p&gt;
&lt;p&gt;We are applying craftsmanship at a higher level of abstraction.&lt;/p&gt;
&lt;p&gt;But let&amp;rsquo;s be honest about the uncomfortable part of this question.&lt;/p&gt;
&lt;p&gt;If one developer with AI can now do the work that previously required three, the natural business response is to hire fewer developers. That is already happening. It is likely to continue.&lt;/p&gt;
&lt;p&gt;The optimistic framing — that AI creates more software demand and therefore more developer jobs — is plausible in the long run. But it does not help the developer who is looking for work today, and it does not guarantee the transition is smooth or fair.&lt;/p&gt;
&lt;p&gt;I am not going to pretend the economics are simple.&lt;/p&gt;
&lt;p&gt;What I do believe is that developers who understand systems, take ownership of quality, and use AI as a serious tool will remain competitive for the work that does exist. That work will increasingly require judgment, not just output.&lt;/p&gt;
&lt;p&gt;The shift is real. The disruption is real. But the craft is not going away — it is just raising its entry bar.&lt;/p&gt;
&lt;h2 id="conclusion--my-opinion"&gt;Conclusion – My Opinion&lt;a class="anchor ms-1" href="#conclusion--my-opinion" aria-label="Permalink: Conclusion – My Opinion"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I do not believe AI will eliminate software developers.&lt;/p&gt;
&lt;p&gt;I do believe it will split the field.&lt;/p&gt;
&lt;p&gt;Developers who keep learning, think in systems, and take ownership of outcomes will become more effective — not despite AI, but partly because of it.&lt;/p&gt;
&lt;p&gt;Developers who treat AI as a shortcut, blindly trusting its output without understanding the system underneath, will increasingly struggle to hold ground.&lt;/p&gt;
&lt;p&gt;The disruption happening right now is real. But I think the long-term demand for engineers who can reason clearly, design well, and take responsibility for what they ship is going to hold.&lt;/p&gt;
&lt;p&gt;The path forward is straightforward even if the road is uncertain: stay curious, deepen the fundamentals, use AI seriously, and never stop questioning the output.&lt;/p&gt;
&lt;h2 id="call-to-action"&gt;Call to Action&lt;a class="anchor ms-1" href="#call-to-action" aria-label="Permalink: Call to Action"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As mentioned at the start of this article, I am a .NET developer looking for my next role. If you know of any opportunities where I could help, please reach out.&lt;/p&gt;
&lt;p&gt;And if you are in the same position, message me as well. We are stronger when we support each other.&lt;/p&gt;</description></item><item><title>ACR vs ECR: Azure and AWS registries</title><link>https://blog-dev.funkysi1701.com/posts/2026/acr-vs-ecr/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 23 Feb 2026 20:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/acr-vs-ecr/</guid><category term="Azure">Azure</category><category term="AWS">AWS</category><category term="Docker">Docker</category><category term="Containers">Containers</category><category term="DevOps">DevOps</category><category term="ACR">ACR</category><category term="ECR">ECR</category><category term="Cloud">Cloud</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/acr-vs-ecr.png"/><description>&lt;p&gt;As someone who works with both Azure and AWS regularly, I&amp;rsquo;ve had hands-on experience with both &lt;strong&gt;Azure Container Registry (ACR)&lt;/strong&gt; and &lt;strong&gt;AWS Elastic Container Registry (ECR)&lt;/strong&gt;. Recently, while migrating my blog&amp;rsquo;s deployment pipeline to use ECR, I encountered some interesting differences that are worth sharing.&lt;/p&gt;
&lt;h2 id="the-basics"&gt;The Basics&lt;a class="anchor ms-1" href="#the-basics" aria-label="Permalink: The Basics"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both services provide secure, private Docker container registries that integrate seamlessly with their respective cloud ecosystems. They&amp;rsquo;re designed to store, manage, and deploy container images for your applications.&lt;/p&gt;
&lt;h3 id="azure-container-registry-acr"&gt;Azure Container Registry (ACR)&lt;a class="anchor ms-1" href="#azure-container-registry-acr" aria-label="Permalink: Azure Container Registry (ACR)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fully managed Docker registry service&lt;/li&gt;
&lt;li&gt;Integrated with Azure Kubernetes Service (AKS), Azure Container Instances, and other Azure services&lt;/li&gt;
&lt;li&gt;Supports Docker images and OCI artifacts&lt;/li&gt;
&lt;li&gt;Available in multiple tiers: Basic, Standard, Premium&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aws-elastic-container-registry-ecr"&gt;AWS Elastic Container Registry (ECR)&lt;a class="anchor ms-1" href="#aws-elastic-container-registry-ecr" aria-label="Permalink: AWS Elastic Container Registry (ECR)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fully managed Docker container registry&lt;/li&gt;
&lt;li&gt;Integrated with Amazon ECS, EKS, and AWS Lambda&lt;/li&gt;
&lt;li&gt;Supports Docker images and OCI artifacts&lt;/li&gt;
&lt;li&gt;Single pricing model with pay-as-you-go&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;OCI artifacts&lt;/strong&gt; are container-related objects that follow the Open Container Initiative (OCI) specifications. Originally, OCI defined standards for Docker container images, but now it covers a broader range of artifacts—including Helm charts, software bill of materials (SBOMs), and other files—stored in container registries.&lt;/p&gt;
&lt;h2 id="pricing-comparison"&gt;Pricing Comparison&lt;a class="anchor ms-1" href="#pricing-comparison" aria-label="Permalink: Pricing Comparison"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is where things get interesting.&lt;/p&gt;
&lt;h3 id="azure-container-registry"&gt;Azure Container Registry&lt;a class="anchor ms-1" href="#azure-container-registry" aria-label="Permalink: Azure Container Registry"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ACR uses a &lt;strong&gt;tiered pricing model&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Basic&lt;/strong&gt;: £4.23/month + storage (£0.083/GB) + bandwidth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standard&lt;/strong&gt;: £16.93/month + storage (£0.083/GB) + bandwidth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Premium&lt;/strong&gt;: £42.32/month + storage (£0.083/GB) + bandwidth + geo-replication&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Premium tier adds features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Geo-replication across Azure regions&lt;/li&gt;
&lt;li&gt;Content trust for image signing&lt;/li&gt;
&lt;li&gt;Private link with private endpoints&lt;/li&gt;
&lt;li&gt;Enhanced throughput&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aws-elastic-container-registry"&gt;AWS Elastic Container Registry&lt;a class="anchor ms-1" href="#aws-elastic-container-registry" aria-label="Permalink: AWS Elastic Container Registry"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ECR uses &lt;strong&gt;simple pay-as-you-go pricing&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage&lt;/strong&gt;: $0.10/GB per month (£0.08/GB)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Transfer&lt;/strong&gt;: Standard AWS data transfer pricing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No base fee&lt;/strong&gt; - you only pay for what you use&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="authentication--setup"&gt;Authentication &amp; Setup&lt;a class="anchor ms-1" href="#authentication--setup" aria-label="Permalink: Authentication &amp;amp; Setup"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is where I hit some friction with ECR.&lt;/p&gt;
&lt;h3 id="azure-container-registry-1"&gt;Azure Container Registry&lt;a class="anchor ms-1" href="#azure-container-registry-1" aria-label="Permalink: Azure Container Registry"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ACR authentication is straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Login using Azure CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az acr login --name myregistry
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Or use service principal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker login myregistry.azurecr.io -u $SP_ID -p $SP_PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# In pipelines, it&amp;#39;s seamless with Azure DevOps tasks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Azure DevOps integration is particularly smooth - the &lt;code&gt;Docker@2&lt;/code&gt; task handles authentication automatically when using service connections.&lt;/p&gt;
&lt;h3 id="aws-elastic-container-registry-1"&gt;AWS Elastic Container Registry&lt;a class="anchor ms-1" href="#aws-elastic-container-registry-1" aria-label="Permalink: AWS Elastic Container Registry"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ECR authentication requires an extra step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get login password and pipe to docker login&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws ecr get-login-password --region eu-north-1 | &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; docker login --username AWS --password-stdin &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 111111111111.dkr.ecr.eu-north-1.amazonaws.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my Azure Pipelines, I had to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install AWS CLI (not included by default)&lt;/li&gt;
&lt;li&gt;Configure AWS credentials as environment variables&lt;/li&gt;
&lt;li&gt;Run the login command manually&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;task&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;CmdLine@2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;displayName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Install AWS CLI&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;script&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; curl &amp;#34;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&amp;#34; -o &amp;#34;awscliv2.zip&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; unzip -q awscliv2.zip
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ./aws/install --bin-dir ~/.local/bin --install-dir ~/.local/aws-cli
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;task&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;CmdLine@2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;displayName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Login to ECR&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;$(AWS_ACCESS_KEY_ID)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;$(AWS_SECRET_ACCESS_KEY)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;script&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; export PATH=$HOME/.local/bin:$PATH
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; aws ecr get-login-password --region eu-north-1 | \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker login --username AWS --password-stdin \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; 111111111111.dkr.ecr.eu-north-1.amazonaws.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: ACR (simpler authentication, especially in Azure DevOps)&lt;/p&gt;
&lt;h2 id="image-naming--urls"&gt;Image Naming &amp; URLs&lt;a class="anchor ms-1" href="#image-naming--urls" aria-label="Permalink: Image Naming &amp;amp; URLs"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="azure-container-registry-2"&gt;Azure Container Registry&lt;a class="anchor ms-1" href="#azure-container-registry-2" aria-label="Permalink: Azure Container Registry"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Clean, predictable naming:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;myregistry.azurecr.io/myapp:v1.0.0
myregistry.azurecr.io/namespace/myapp:latest
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="aws-elastic-container-registry-2"&gt;AWS Elastic Container Registry&lt;a class="anchor ms-1" href="#aws-elastic-container-registry-2" aria-label="Permalink: AWS Elastic Container Registry"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Includes your AWS account ID:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;111111111111.dkr.ecr.eu-north-1.amazonaws.com/funkysi1701/blog:10.1.1.123-develop
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The account ID in the URL may pose a security consideration - it&amp;rsquo;s visible to anyone who has access to your images or deployment configs. Attackers could use the account ID for targeted phishing, social engineering, or brute-force attacks. If combined with leaked credentials or misconfigured permissions, it makes it easier for someone to identify and target your AWS resources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: ACR (cleaner URLs)&lt;/p&gt;
&lt;h2 id="features-comparison"&gt;Features Comparison&lt;a class="anchor ms-1" href="#features-comparison" aria-label="Permalink: Features Comparison"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ACR&lt;/th&gt;
&lt;th&gt;ECR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image Scanning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Premium tier&lt;/td&gt;
&lt;td&gt;✅ Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vulnerability Scanning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Premium tier&lt;/td&gt;
&lt;td&gt;✅ Basic + Enhanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Geo-Replication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Premium tier&lt;/td&gt;
&lt;td&gt;❌ Manual setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Webhooks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ All tiers&lt;/td&gt;
&lt;td&gt;✅ Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image Retention Policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ All tiers&lt;/td&gt;
&lt;td&gt;✅ Lifecycle policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Private Endpoints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Premium tier&lt;/td&gt;
&lt;td&gt;✅ VPC endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image Signing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Content Trust&lt;/td&gt;
&lt;td&gt;✅ AWS Signer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Region Replication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Premium&lt;/td&gt;
&lt;td&gt;❌ Requires manual setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Import from Docker Hub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="integration-with-kubernetes"&gt;Integration with Kubernetes&lt;a class="anchor ms-1" href="#integration-with-kubernetes" aria-label="Permalink: Integration with Kubernetes"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="acr--aks"&gt;ACR + AKS&lt;a class="anchor ms-1" href="#acr--aks" aria-label="Permalink: ACR + AKS"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Seamless integration with Azure Kubernetes Service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Attach ACR to AKS cluster&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;az aks update --name myaks --resource-group mygroup --attach-acr myregistry
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;AKS nodes can pull images without credentials. Magical.&lt;/p&gt;
&lt;h3 id="ecr--eks"&gt;ECR + EKS&lt;a class="anchor ms-1" href="#ecr--eks" aria-label="Permalink: ECR + EKS"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Also integrated, but requires IAM roles:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Attach IAM policy to EKS node role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws iam attach-role-policy &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --role-name eksNodeRole &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works well once configured, but requires understanding of AWS IAM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Winner&lt;/strong&gt;: Tie (both integrate well with their respective K8s offerings)&lt;/p&gt;
&lt;h2 id="developer-experience"&gt;Developer Experience&lt;a class="anchor ms-1" href="#developer-experience" aria-label="Permalink: Developer Experience"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="what-i-like-about-acr"&gt;What I Like About ACR&lt;a class="anchor ms-1" href="#what-i-like-about-acr" aria-label="Permalink: What I Like About ACR"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Simple authentication in Azure DevOps&lt;/li&gt;
&lt;li&gt;Clean, readable image URLs&lt;/li&gt;
&lt;li&gt;Excellent documentation&lt;/li&gt;
&lt;li&gt;Azure Portal UI is intuitive&lt;/li&gt;
&lt;li&gt;Helm chart support is first-class&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-i-like-about-ecr"&gt;What I Like About ECR&lt;a class="anchor ms-1" href="#what-i-like-about-ecr" aria-label="Permalink: What I Like About ECR"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;No minimum cost - truly pay-per-use&lt;/li&gt;
&lt;li&gt;Built-in vulnerability scanning at all levels&lt;/li&gt;
&lt;li&gt;AWS CLI is powerful and ubiquitous&lt;/li&gt;
&lt;li&gt;Great for multi-cloud strategies&lt;/li&gt;
&lt;li&gt;Excellent API and automation support&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="pain-points"&gt;Pain Points&lt;a class="anchor ms-1" href="#pain-points" aria-label="Permalink: Pain Points"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;ACR&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Premium tier gets expensive for features that should be standard&lt;/li&gt;
&lt;li&gt;Minimum £4/month even for tiny projects&lt;/li&gt;
&lt;li&gt;Geo-replication requires Premium tier (£42/month)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Geo-replication is important because it allows your container images and artifacts to be stored and accessed in multiple geographic regions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ECR&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication is more complex outside AWS&lt;/li&gt;
&lt;li&gt;Account ID in image URL&lt;/li&gt;
&lt;li&gt;Requires AWS CLI installation in non-AWS CI/CD&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="my-real-world-experience"&gt;My Real-World Experience&lt;a class="anchor ms-1" href="#my-real-world-experience" aria-label="Permalink: My Real-World Experience"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For my blog&amp;rsquo;s deployment pipeline, I recently migrated from ACR to ECR primarily for &lt;strong&gt;cost reasons&lt;/strong&gt;. Here&amp;rsquo;s what I learned:&lt;/p&gt;
&lt;h3 id="the-migration"&gt;The Migration&lt;a class="anchor ms-1" href="#the-migration" aria-label="Permalink: The Migration"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Before (ACR)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cost: ~£4.50/month (Basic tier + minimal storage)&lt;/li&gt;
&lt;li&gt;Authentication: Seamless in Azure DevOps&lt;/li&gt;
&lt;li&gt;Image URLs: Clean and simple&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;After (ECR)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cost: £0/month (due to free trial)&lt;/li&gt;
&lt;li&gt;Authentication: Required custom pipeline steps&lt;/li&gt;
&lt;li&gt;Image URLs: Include AWS account ID&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="was-it-worth-it"&gt;Was It Worth It?&lt;a class="anchor ms-1" href="#was-it-worth-it" aria-label="Permalink: Was It Worth It?"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For my small personal project, &lt;strong&gt;yes&lt;/strong&gt; - saving money is meaningful. But the setup was more complex than I expected.&lt;/p&gt;
&lt;p&gt;For enterprise workloads, I&amp;rsquo;d still choose ACR Premium if I needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Geo-replication&lt;/li&gt;
&lt;li&gt;Content trust&lt;/li&gt;
&lt;li&gt;Azure-native integration&lt;/li&gt;
&lt;li&gt;Enterprise support&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="recommendations"&gt;Recommendations&lt;a class="anchor ms-1" href="#recommendations" aria-label="Permalink: Recommendations"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="choose-acr-if"&gt;Choose ACR if&lt;a class="anchor ms-1" href="#choose-acr-if" aria-label="Permalink: Choose ACR if"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;✅ You&amp;rsquo;re heavily invested in Azure ecosystem&lt;br&gt;
✅ You need geo-replication&lt;br&gt;
✅ You want seamless AKS integration&lt;br&gt;
✅ You value simplified authentication&lt;br&gt;
✅ You need Azure-native compliance features&lt;/p&gt;
&lt;h3 id="choose-ecr-if"&gt;Choose ECR if&lt;a class="anchor ms-1" href="#choose-ecr-if" aria-label="Permalink: Choose ECR if"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;✅ You want zero minimum costs&lt;br&gt;
✅ You&amp;rsquo;re on AWS or multi-cloud&lt;br&gt;
✅ You need built-in vulnerability scanning&lt;br&gt;
✅ You prefer pay-per-use pricing&lt;br&gt;
✅ You&amp;rsquo;re comfortable with IAM and AWS CLI&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="anchor ms-1" href="#conclusion" aria-label="Permalink: Conclusion"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both ACR and ECR are excellent services. Your choice should depend on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Your cloud platform&lt;/strong&gt; - Use the registry that matches your deployment target&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your budget&lt;/strong&gt; - ECR wins for small projects, ACR Premium for enterprise features&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your team&amp;rsquo;s expertise&lt;/strong&gt; - Stick with what your team knows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your requirements&lt;/strong&gt; - Need geo-replication? ACR Premium. Need low-cost? ECR.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For my personal projects, I&amp;rsquo;m happy with ECR&amp;rsquo;s cost savings. For enterprise work, I still recommend ACR Premium for its advanced features and Azure integration.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s your experience with container registries? Let me know in the comments!&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources&lt;a class="anchor ms-1" href="#resources" aria-label="Permalink: Resources"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/container-registry/" target="_blank" rel="noopener noreferrer"&gt;Azure Container Registry Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/ecr/" target="_blank" rel="noopener noreferrer"&gt;AWS ECR Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-gb/pricing/details/container-registry/" target="_blank" rel="noopener noreferrer"&gt;ACR Pricing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/ecr/pricing/" target="_blank" rel="noopener noreferrer"&gt;ECR Pricing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>NDC London 2026</title><link>https://blog-dev.funkysi1701.com/posts/2026/ndc-london-2026/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Mon, 02 Feb 2026 20:46:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/ndc-london-2026/</guid><category term="NDCLondon">NDCLondon</category><category term="Conference">Conference</category><category term="DotNet">DotNet</category><category term="AI">AI</category><category term="Accessibility">Accessibility</category><category term="WCAG">WCAG</category><category term="Playwright">Playwright</category><category term="Testing">Testing</category><category term="Azure">Azure</category><category term="OpenTelemetry">OpenTelemetry</category><category term="UX">UX</category><category term="Volunteering">Volunteering</category><category term="2026">2026</category><media:content medium="image" type="image/jpeg" url="https://blog-dev.funkysi1701.com/images/2026/ndc-me.jpg"/><description>&lt;img src="https://blog-dev.funkysi1701.com/images/2026/ndc-me.jpg" alt="Volunteering at NDC London 2026" width="250px" style="float:left;padding-right: 15px; " /&gt;
&lt;p&gt;For the second year running, I had the privilege of volunteering at NDC London – one of Europe&amp;rsquo;s premier software development conferences. This year&amp;rsquo;s event was packed with insights on AI, accessibility, testing automation, and the future of software development. If you&amp;rsquo;re interested in my first experience, check out my &lt;a href="https://blog-dev.funkysi1701.com/posts/2025/volunteering-at-ndc/"&gt;2025 report&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;The volunteer experience was just as rewarding as last year – the team was fantastic, the energy was incredible, and the sense of community was palpable. In this post, I&amp;rsquo;ll focus on the talks I attended and the key insights I gained from them.&lt;/p&gt;
&lt;p&gt;As I mentioned last year, volunteering gave me a &lt;strong&gt;superpower&lt;/strong&gt;: increased confidence to network with speakers, vendors, fellow volunteers, and attendees. The connections you make at conferences like this are invaluable, and being part of the team made it even easier to engage with the community.&lt;/p&gt;
&lt;h2 id="wednesday"&gt;Wednesday&lt;a class="anchor ms-1" href="#wednesday" aria-label="Permalink: Wednesday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="lets-break-some-wcag-rules-speaker-elise-kristiansen"&gt;Let’s Break Some WCAG Rules (Speaker: Elise Kristiansen)&lt;a class="anchor ms-1" href="#lets-break-some-wcag-rules-speaker-elise-kristiansen" aria-label="Permalink: Let’s Break Some WCAG Rules (Speaker: Elise Kristiansen)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This eye-opening talk examined how pervasively websites violate Web Content Accessibility Guidelines (WCAG). For those unfamiliar, WCAG provides a framework to make web content accessible to everyone, including people with disabilities. The guidelines are organized into three conformance levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Level A&lt;/strong&gt; - The bare minimum requirements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Level AA&lt;/strong&gt; - The legal requirement (what most laws mandate)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Level AAA&lt;/strong&gt; - The gold standard, aspirational but not legally required&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The statistics Elise shared were sobering: 15-25% of people have some form of disability (whether permanent, temporary, or situational), yet a staggering &lt;strong&gt;94.8% of websites contain at least one WCAG AA violation&lt;/strong&gt; – meaning they&amp;rsquo;re actually breaking the law.&lt;/p&gt;
&lt;p&gt;One of the most powerful demonstrations was a colour blindness test where only colour blind individuals could read certain numbers. It really drove home how design choices can inadvertently exclude significant portions of our users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WCAG compliance isn&amp;rsquo;t optional – it&amp;rsquo;s a legal requirement and moral imperative&lt;/li&gt;
&lt;li&gt;Stop using &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for everything; HTML is semantically rich for a reason&lt;/li&gt;
&lt;li&gt;Consider learning to use a screen reader to understand user experiences better&lt;/li&gt;
&lt;li&gt;Accessibility benefits everyone, not just those with disabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="social-engineering-hacking-humans-speaker-pawel-sucholbiak"&gt;Social Engineering: Hacking Humans (Speaker: Pawel Sucholbiak)&lt;a class="anchor ms-1" href="#social-engineering-hacking-humans-speaker-pawel-sucholbiak" aria-label="Permalink: Social Engineering: Hacking Humans (Speaker: Pawel Sucholbiak)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A timely reminder that as soon as humans enter the equation, social engineering becomes a significant security risk. This talk explored various tactics malicious actors use to manipulate people into divulging confidential information or performing actions that compromise security.&lt;/p&gt;
&lt;p&gt;Pawel demonstrated real-world scenarios like phishing emails that appear to come from trusted colleagues, pretexting phone calls where attackers impersonate IT support, and how seemingly innocent information shared on social media can be weaponized. The human element remains the weakest link in any security chain.&lt;/p&gt;
&lt;h3 id="supercharged-testing-ai-powered-workflows-with-playwright--mcp-speaker-debbie-obrien"&gt;Supercharged Testing: AI-Powered Workflows with Playwright + MCP (Speaker: Debbie O’Brien)&lt;a class="anchor ms-1" href="#supercharged-testing-ai-powered-workflows-with-playwright--mcp-speaker-debbie-obrien" aria-label="Permalink: Supercharged Testing: AI-Powered Workflows with Playwright + MCP (Speaker: Debbie O’Brien)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;d heard Debbie discuss the Playwright MCP (Model Context Protocol) on .NET Rocks, but seeing it demonstrated live was transformative. This session showed how AI can dramatically enhance testing workflows by understanding test intent and generating Playwright test code.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve already started using it to write some tests for this very website!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Experiment with the Playwright MCP – it&amp;rsquo;s a game-changer for test automation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="the-undersea-cable-network-speaker-richard-campbell"&gt;The Undersea Cable Network (Speaker: Richard Campbell)&lt;a class="anchor ms-1" href="#the-undersea-cable-network-speaker-richard-campbell" aria-label="Permalink: The Undersea Cable Network (Speaker: Richard Campbell)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Richard took us on a fascinating journey through the history of undersea telecommunications cables – the literal backbone of the internet. He explored how these cables are laid, maintained, and the significant challenges when they&amp;rsquo;re damaged (whether accidental or deliberate). It&amp;rsquo;s humbling to realize that our globally connected world depends on physical cables crossing ocean floors.&lt;/p&gt;
&lt;h3 id="java-sucks-so-c-didnt-have-to-speaker-adele-carpenter"&gt;Java Sucks (So C# Didn’t Have To) (Speaker: Adele Carpenter)&lt;a class="anchor ms-1" href="#java-sucks-so-c-didnt-have-to-speaker-adele-carpenter" aria-label="Permalink: Java Sucks (So C# Didn’t Have To) (Speaker: Adele Carpenter)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An entertaining and insightful exploration of Java&amp;rsquo;s history, examining how its early popularity locked in certain design decisions that became problematic over time. Adele showed how C# learned from Java&amp;rsquo;s mistakes and made different choices that enabled better evolution. The key lesson: early success can create technical debt that&amp;rsquo;s nearly impossible to pay down.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://speakerdeck.com/97adele/java-sucks-so-c-number-didnt-have-to" target="_blank" rel="noopener noreferrer"&gt;View the slides&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="think-like-a-user-practical-ux-design-tips-for-developers-speaker-lex-lofthouse"&gt;Think Like a User: Practical UX Design Tips for Developers (Speaker: Lex Lofthouse)&lt;a class="anchor ms-1" href="#think-like-a-user-practical-ux-design-tips-for-developers-speaker-lex-lofthouse" aria-label="Permalink: Think Like a User: Practical UX Design Tips for Developers (Speaker: Lex Lofthouse)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This session challenged developers to shift perspectives and consider user experience from the ground up. Lex provided practical, actionable advice for creating more intuitive interfaces.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.figma.com/deck/FgeSzQhT74JlnxW3iiONqj/UX-Tips?node-id=1-42&amp;amp;t=h9PIcTBhU6KRDMNU-1" target="_blank" rel="noopener noreferrer"&gt;View the slides&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recommended Reading:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The Design of Everyday Things&lt;/em&gt; by Don Norman&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="thursday"&gt;Thursday&lt;a class="anchor ms-1" href="#thursday" aria-label="Permalink: Thursday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="opentelemetry-at-scale-101-intro-to-opamp-speakers-aakansha-priya-and-adriana-villela"&gt;OpenTelemetry At Scale 101: Intro to OpAMP (Speakers: Aakansha Priya and Adriana Villela)&lt;a class="anchor ms-1" href="#opentelemetry-at-scale-101-intro-to-opamp-speakers-aakansha-priya-and-adriana-villela" aria-label="Permalink: OpenTelemetry At Scale 101: Intro to OpAMP (Speakers: Aakansha Priya and Adriana Villela)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This session introduced OpAMP (Open Agent Management Protocol) for managing OpenTelemetry collectors at scale. While the scale discussed was beyond my current needs, it was valuable to understand the challenges and solutions for production observability in large distributed systems.&lt;/p&gt;
&lt;h3 id="a-defence-of-technical-excellence-speaker-chris-simon"&gt;A Defence of Technical Excellence (Speaker: Chris Simon)&lt;a class="anchor ms-1" href="#a-defence-of-technical-excellence-speaker-chris-simon" aria-label="Permalink: A Defence of Technical Excellence (Speaker: Chris Simon)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chris made a compelling argument for maintaining high technical standards even under pressure to deliver quickly. Technical excellence isn&amp;rsquo;t about perfectionism – it&amp;rsquo;s about sustainable, maintainable code that serves the business long-term.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://chrissimon.au/speaking/talks/a-defence-of-technical-excellence/?read-more=1" target="_blank" rel="noopener noreferrer"&gt;Read more about this talk&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id="how-to-git-away-with-murder-speaker-sergès-goma"&gt;How to Git Away with Murder (Speaker: Sergès Goma)&lt;a class="anchor ms-1" href="#how-to-git-away-with-murder-speaker-sergès-goma" aria-label="Permalink: How to Git Away with Murder (Speaker: Sergès Goma)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A humorous yet practical look at Git workflows and how to recover from common (and not-so-common) Git disasters. The Q&amp;amp;A session at the end turned into a group therapy session where attendees shared their most spectacular Git mishaps and recovery stories. If you&amp;rsquo;ve ever accidentally force-pushed to main, this talk was for you!&lt;/p&gt;
&lt;h3 id="code-that-writes-code-net-source-generators-speaker-glenn-f-henriksen"&gt;Code That Writes Code: .NET Source Generators (Speaker: Glenn F. Henriksen)&lt;a class="anchor ms-1" href="#code-that-writes-code-net-source-generators-speaker-glenn-f-henriksen" aria-label="Permalink: Code That Writes Code: .NET Source Generators (Speaker: Glenn F. Henriksen)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Source generators are one of .NET&amp;rsquo;s most powerful yet underutilized features. Glenn demonstrated how they can eliminate boilerplate, improve performance by moving work to compile-time, and create type-safe code generation patterns. This is definitely an area I need to explore more for my own projects.&lt;/p&gt;
&lt;h3 id="warm-and-fuzzy-semantic-search-in-net-speaker-jonathan-j-tower"&gt;Warm and Fuzzy: Semantic Search in .NET (Speaker: Jonathan “J.” Tower)&lt;a class="anchor ms-1" href="#warm-and-fuzzy-semantic-search-in-net-speaker-jonathan-j-tower" aria-label="Permalink: Warm and Fuzzy: Semantic Search in .NET (Speaker: Jonathan “J.” Tower)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Jonathan delivered an excellent introduction to implementing semantic search using vector databases. He covered several options for .NET developers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQL Server 2025&lt;/strong&gt; with native vector support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Cosmos DB&lt;/strong&gt; for NoSQL vector storage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Qdrant&lt;/strong&gt; as a specialized vector database&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The talk also touched on &lt;strong&gt;Semantic Kernel&lt;/strong&gt;, which is now part of the Microsoft Agent Framework – a significant development for building intelligent applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action Items:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement semantic search for my blog to improve content discovery&lt;/li&gt;
&lt;li&gt;Explore the &lt;a href="https://github.com/trailheadtechnology/dotnet-semantic-search/tree/main/code" target="_blank" rel="noopener noreferrer"&gt;demo code on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="coding-4-fun-8-bit-game-emulation-in-net-speaker-alex-thissen"&gt;Coding 4 Fun: 8-bit Game Emulation in .NET (Speaker: Alex Thissen)&lt;a class="anchor ms-1" href="#coding-4-fun-8-bit-game-emulation-in-net-speaker-alex-thissen" aria-label="Permalink: Coding 4 Fun: 8-bit Game Emulation in .NET (Speaker: Alex Thissen)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pure fun! Alex walked through building an 8-bit game emulator in .NET, demonstrating low-level programming concepts in a modern language. A great reminder that not everything we build needs to be &amp;ldquo;enterprise-ready&amp;rdquo; – sometimes coding is just about learning and enjoyment. Lots of shifting bytes around, so a great example of the different things you can do with .NET.&lt;/p&gt;
&lt;h2 id="friday"&gt;Friday&lt;a class="anchor ms-1" href="#friday" aria-label="Permalink: Friday"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="keynote-ai-powered-app-development-speaker-steve-sanderson"&gt;Keynote: AI-Powered App Development (Speaker: Steve Sanderson)&lt;a class="anchor ms-1" href="#keynote-ai-powered-app-development-speaker-steve-sanderson" aria-label="Permalink: Keynote: AI-Powered App Development (Speaker: Steve Sanderson)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Steve Sanderson delivered an inspiring keynote on how AI is transforming application development. The &lt;a href="https://github.com/features/copilot/cli" target="_blank" rel="noopener noreferrer"&gt;GitHub Copilot CLI&lt;/a&gt;
was demonstrated, with a new feature added in front of our eyes. Hundreds of Pull Requests can be generated with AI, but they all need reviewing and maintaining by Software Developers and there are no easy answers for managing that.&lt;/p&gt;
&lt;h3 id="what-in-the-hunger-games-is-happening-with-recruitment-speaker-suzi-edwards-alexander"&gt;What in the Hunger Games is Happening with Recruitment? (Speaker: Suzi Edwards-Alexander)&lt;a class="anchor ms-1" href="#what-in-the-hunger-games-is-happening-with-recruitment-speaker-suzi-edwards-alexander" aria-label="Permalink: What in the Hunger Games is Happening with Recruitment? (Speaker: Suzi Edwards-Alexander)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A frank and often amusing look at the current state of tech recruitment. Suzi traced how we arrived at today&amp;rsquo;s challenging hiring landscape – from the first interview questions created by Thomas Edison, to the rise of LinkedIn and modern recruitment processes. Recruitment is broken, and there are no easy ways of fixing it.&lt;/p&gt;
&lt;h3 id="the-great-brain-robbery-navigating-the-dark-future-of-online-manipulation-speaker-jeff-watkins"&gt;The Great Brain Robbery: Navigating the Dark Future of Online Manipulation (Speaker: Jeff Watkins)&lt;a class="anchor ms-1" href="#the-great-brain-robbery-navigating-the-dark-future-of-online-manipulation-speaker-jeff-watkins" aria-label="Permalink: The Great Brain Robbery: Navigating the Dark Future of Online Manipulation (Speaker: Jeff Watkins)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A sobering examination of how online platforms use psychological manipulation and dark patterns to exploit users&amp;rsquo; attention and data. Jeff didn&amp;rsquo;t just highlight the problems – he offered strategies for recognizing and resisting these tactics, both as developers and as users. This talk was rather bleak reinforcing the notion that you can not trust anything you see or read online.&lt;/p&gt;
&lt;h3 id="beyond-the-ai-hype-whats-real-whats-next-speaker-richard-campbell"&gt;Beyond the AI Hype: What’s Real, What’s Next (Speaker: Richard Campbell)&lt;a class="anchor ms-1" href="#beyond-the-ai-hype-whats-real-whats-next-speaker-richard-campbell" aria-label="Permalink: Beyond the AI Hype: What’s Real, What’s Next (Speaker: Richard Campbell)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Richard cut through the AI hype to examine what&amp;rsquo;s genuinely transformative versus what&amp;rsquo;s just marketing. He explored current capabilities, realistic near-term developments, and the fundamental limitations we need to understand. A perfect counterbalance to the morning&amp;rsquo;s optimistic keynote – both perspectives are necessary for making informed decisions about AI adoption.&lt;/p&gt;
&lt;h3 id="resilient-by-design-speaker-chris-ayers"&gt;Resilient by Design (Speaker: Chris Ayers)&lt;a class="anchor ms-1" href="#resilient-by-design-speaker-chris-ayers" aria-label="Permalink: Resilient by Design (Speaker: Chris Ayers)"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The final session I attended focused on building resilient systems in Azure from the ground up. Chris covered how much downtime you can have if your SLA is 99.99% (8.6s a day see &lt;a href="https://uptime.is" target="_blank" rel="noopener noreferrer"&gt;uptime.is&lt;/a&gt;
) – but more importantly, the mindset shift required to design for failure rather than just success. In production, it&amp;rsquo;s not &lt;em&gt;if&lt;/em&gt; things will fail, but &lt;em&gt;when&lt;/em&gt; and &lt;em&gt;how gracefully&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="networking-break-jetbrains-rider-team"&gt;Networking Break: JetBrains Rider Team&lt;a class="anchor ms-1" href="#networking-break-jetbrains-rider-team" aria-label="Permalink: Networking Break: JetBrains Rider Team"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I skipped one session to have an extended conversation with the &lt;a href="https://www.jetbrains.com/rider/" target="_blank" rel="noopener noreferrer"&gt;JetBrains Rider&lt;/a&gt;
team at their booth. These unstructured conversations are often where you get the most value at conferences – I am a big fan of Visual Studio, but I have never tried Rider, this informal chat has encouraged me to download Rider and give it a try.&lt;/p&gt;
&lt;h2 id="final-thoughts"&gt;Final Thoughts&lt;a class="anchor ms-1" href="#final-thoughts" aria-label="Permalink: Final Thoughts"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;NDC London 2026 was another incredible experience. The breadth of topics – from accessibility and UX to AI, observability, and system resilience – reflects the diverse skills modern developers need.&lt;/p&gt;
&lt;p&gt;My key takeaways from this year:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Accessibility is non-negotiable&lt;/strong&gt; – both legally and morally&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI tools are here to stay&lt;/strong&gt; – learn to leverage them effectively&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technical excellence matters&lt;/strong&gt; – shortcuts today become tomorrow&amp;rsquo;s technical debt&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community is everything&lt;/strong&gt; – the conversations between sessions were as valuable as the talks themselves&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Huge thanks to the NDC organizing team, my fellow volunteers, all the speakers who shared their knowledge, and the attendees who made this such an engaging event. If you get the chance to attend (or volunteer at) &lt;a href="https://ndcconferences.com/" target="_blank" rel="noopener noreferrer"&gt;NDC Conferences&lt;/a&gt;
, I highly recommend it.&lt;/p&gt;
&lt;p&gt;Interested in attending NDC London 2027? Follow &lt;a href="https://twitter.com/NDC_Conferences" target="_blank" rel="noopener noreferrer"&gt;@NDC_Conferences&lt;/a&gt;
for updates!&lt;/p&gt;
&lt;p&gt;See you at NDC London 2027! 🚀&lt;/p&gt;
&lt;p&gt;
&lt;img class="img-fluid" alt="The Awesome Volunteer Team at NDC London" src="https://blog-dev.funkysi1701.com/images/2026/ndc-team.jpg" loading="lazy"
width="2048" height="1536"
/&gt;
&lt;/p&gt;</description></item><item><title>2025 in Review and Goals for 2026</title><link>https://blog-dev.funkysi1701.com/posts/2026/2025-in-review-and-2026-goals/</link><author>funkysi1701@gmail.com (funkysi1701)</author><pubDate>Thu, 01 Jan 2026 09:00:00 +0000</pubDate><guid>https://blog-dev.funkysi1701.com/posts/2026/2025-in-review-and-2026-goals/</guid><category term="Retrospective">Retrospective</category><category term="Goals">Goals</category><category term="YearlyReview">YearlyReview</category><category term="Blogging">Blogging</category><media:content medium="image" type="image/png" url="https://blog-dev.funkysi1701.com/images/2026/welcome-2026.png"/><description>&lt;h2 id="2025-in-review"&gt;2025 in Review&lt;a class="anchor ms-1" href="#2025-in-review" aria-label="Permalink: 2025 in Review"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What a year 2025 has been! I published 24 blog posts covering a wide range of topics from cloud-native technologies to AI experiments, and even some personal reflections. Here are the key themes and highlights from my blogging year:&lt;/p&gt;
&lt;h3 id="cloud-native-and-devops"&gt;Cloud-Native and DevOps&lt;a class="anchor ms-1" href="#cloud-native-and-devops" aria-label="Permalink: Cloud-Native and DevOps"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This was a major focus area for me in 2025, not really surprising as I moved into a DevOps role this year, with deep dives into modern infrastructure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/learning-kubernetes/"&gt;&lt;strong&gt;Learning Kubernetes&lt;/strong&gt;&lt;/a&gt;
- Started my journey into container orchestration and shared my beginner experiences&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/deploying-hugo-with-helm/"&gt;&lt;strong&gt;Deploying Hugo with Helm&lt;/strong&gt;&lt;/a&gt;
- Deployed this very blog to Kubernetes using Helm charts&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/kubernetes-and-letsencrypt/"&gt;&lt;strong&gt;Kubernetes and Let&amp;rsquo;s Encrypt&lt;/strong&gt;&lt;/a&gt;
- Tackled SSL certificate management in Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/monitoring-with-nagios-docker/"&gt;&lt;strong&gt;Monitoring with Nagios Docker&lt;/strong&gt;&lt;/a&gt;
- Explored monitoring solutions in containerized environments&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="net-and-development"&gt;.NET and Development&lt;a class="anchor ms-1" href="#net-and-development" aria-label="Permalink: .NET and Development"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My bread and butter continues to be .NET development, with several posts exploring new features and tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/blazor-and-dotnet10/"&gt;&lt;strong&gt;Blazor and .NET 10&lt;/strong&gt;&lt;/a&gt;
- Covered breaking changes, fixes, and new features in the latest .NET release&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/adding-elasticsearch-with-aspire/"&gt;&lt;strong&gt;Adding Elasticsearch with .NET Aspire&lt;/strong&gt;&lt;/a&gt;
- Integrated search capabilities using Aspire&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/aspire-9.2/"&gt;&lt;strong&gt;Aspire 9.2&lt;/strong&gt;&lt;/a&gt;
- Explored the latest Aspire release&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/nuget-central-package-management/"&gt;&lt;strong&gt;NuGet Central Package Management&lt;/strong&gt;&lt;/a&gt;
- Streamlined dependency management&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/whats-new-csharp/"&gt;&lt;strong&gt;What&amp;rsquo;s New in C#&lt;/strong&gt;&lt;/a&gt;
- Covered the latest language features&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/exceptions/"&gt;&lt;strong&gt;Exceptions&lt;/strong&gt;&lt;/a&gt;
- Best practices for error handling&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="observability-and-monitoring"&gt;Observability and Monitoring&lt;a class="anchor ms-1" href="#observability-and-monitoring" aria-label="Permalink: Observability and Monitoring"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Understanding what&amp;rsquo;s happening in production became a key theme:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/getting-started-with-opentelemetry/"&gt;&lt;strong&gt;Getting Started with OpenTelemetry&lt;/strong&gt;&lt;/a&gt;
- Made observability easy with modern tracing&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/opentelemetry-logs/"&gt;&lt;strong&gt;OpenTelemetry Logs&lt;/strong&gt;&lt;/a&gt;
- Extended observability to include logging&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/setting-up-grafana/"&gt;&lt;strong&gt;Setting up Grafana&lt;/strong&gt;&lt;/a&gt;
- Built dashboards for monitoring&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cloud-platforms"&gt;Cloud Platforms&lt;a class="anchor ms-1" href="#cloud-platforms" aria-label="Permalink: Cloud Platforms"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/merge-two-projects-into-one/"&gt;&lt;strong&gt;Merging Two Projects into One&lt;/strong&gt;&lt;/a&gt;
- Shared my experience consolidating codebases&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ai-and-creativity"&gt;AI and Creativity&lt;a class="anchor ms-1" href="#ai-and-creativity" aria-label="Permalink: AI and Creativity"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/fun-with-ai/"&gt;&lt;strong&gt;Fun with AI&lt;/strong&gt;&lt;/a&gt;
- Explored ChatGPT&amp;rsquo;s image generation capabilities&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/using-ai/"&gt;&lt;strong&gt;Using AI&lt;/strong&gt;&lt;/a&gt;
- Practical applications of AI in daily work&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/mandelbrot-set/"&gt;&lt;strong&gt;Mandelbrot Set&lt;/strong&gt;&lt;/a&gt;
- A fun mathematical exploration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="professional-development"&gt;Professional Development&lt;a class="anchor ms-1" href="#professional-development" aria-label="Permalink: Professional Development"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/pragmatic-programmer/"&gt;&lt;strong&gt;The Pragmatic Programmer&lt;/strong&gt;&lt;/a&gt;
- Book review of this classic software development book&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/the-hacker-ethic/"&gt;&lt;strong&gt;The Hacker Ethic&lt;/strong&gt;&lt;/a&gt;
- Reflections on developer culture and values&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/stepping-outside-your-comfort-zone/"&gt;&lt;strong&gt;Stepping Outside Your Comfort Zone&lt;/strong&gt;&lt;/a&gt;
- Personal growth and career development&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/volunteering-at-ndc/"&gt;&lt;strong&gt;Volunteering at NDC London&lt;/strong&gt;&lt;/a&gt;
- My experience at this major .NET conference&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/periodic-table-devops-2025/"&gt;&lt;strong&gt;Periodic Table of DevOps 2025&lt;/strong&gt;&lt;/a&gt;
- A visual guide to the DevOps ecosystem&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="fun-projects"&gt;Fun Projects&lt;a class="anchor ms-1" href="#fun-projects" aria-label="Permalink: Fun Projects"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog-dev.funkysi1701.com/posts/2025/festive-naughty-or-nice-checker/"&gt;&lt;strong&gt;Festive Naughty or Nice Checker&lt;/strong&gt;&lt;/a&gt;
- A seasonal AI-powered fun project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you to everyone who read, commented, and shared my posts throughout the year. Your engagement and feedback mean the world to me!&lt;/p&gt;
&lt;h2 id="2025-by-the-numbers"&gt;2025 by the Numbers&lt;a class="anchor ms-1" href="#2025-by-the-numbers" aria-label="Permalink: 2025 by the Numbers"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;24 blog posts&lt;/strong&gt; published&lt;/li&gt;
&lt;li&gt;Major topics: Kubernetes, .NET, OpenTelemetry, AI&lt;/li&gt;
&lt;li&gt;New skills acquired: Helm, Kubernetes, observability tools&lt;/li&gt;
&lt;li&gt;Conferences attended: NDC London (as a volunteer) and DDD North&lt;/li&gt;
&lt;li&gt;Submitted content to &lt;a href="https://festivetechcalendar.com" target="_blank" rel="noopener noreferrer"&gt;https://festivetechcalendar.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="goals-for-2026"&gt;Goals for 2026&lt;a class="anchor ms-1" href="#goals-for-2026" aria-label="Permalink: Goals for 2026"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Looking ahead to 2026, I&amp;rsquo;m excited to build on this momentum. Here are my blogging and technical goals:&lt;/p&gt;
&lt;h3 id="content-goals"&gt;Content Goals&lt;a class="anchor ms-1" href="#content-goals" aria-label="Permalink: Content Goals"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Publish at least 24 posts&lt;/strong&gt; - Maintain the pace from 2025&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deep dive into AI/ML&lt;/strong&gt; - Go beyond experimentation to practical implementations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More hands-on tutorials&lt;/strong&gt; - Provide step-by-step guides that readers can follow along with&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="technical-skills"&gt;Technical Skills&lt;a class="anchor ms-1" href="#technical-skills" aria-label="Permalink: Technical Skills"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Master Kubernetes&lt;/strong&gt; - Move from beginner to intermediate proficiency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced observability&lt;/strong&gt; - Implement comprehensive monitoring across distributed systems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud-native patterns&lt;/strong&gt; - Explore microservices, event-driven architectures, and service mesh&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI integration&lt;/strong&gt; - Build practical AI-powered features into applications&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform engineering&lt;/strong&gt; - Create better developer experiences and internal platforms&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="walking"&gt;Walking&lt;a class="anchor ms-1" href="#walking" aria-label="Permalink: Walking"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the end of 2024 I made the following goal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2024 took an unusual turn. In June, I walked a half marathon across the Peak District, and since then,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;I have been doing lots of walks. Walking is good for my physical and mental health,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;so I intend to do more of it in 2025. I have a 5k scheduled for January,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;which should be lots of fun as it is around the wildlife park.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well I enjoyed my walk in January 2025, which then led to two 10k walks (in July and September) and a 7.5k night walk (in Blackpool to see the Illuminations). This led me to explore &lt;a href="https://www.parkrun.com/" target="_blank" rel="noopener noreferrer"&gt;parkrun&lt;/a&gt;
and I completed my 8th run this morning. I say run, not walk as I am slowly becoming more of a runner and less of a walker.&lt;/p&gt;
&lt;p&gt;So my goal here is to keep doing this, as I am so far enjoying getting out and about.&lt;/p&gt;
&lt;h2 id="what-would-you-like-to-see"&gt;What Would You Like to See?&lt;a class="anchor ms-1" href="#what-would-you-like-to-see" aria-label="Permalink: What Would You Like to See?"&gt;&lt;i class="fas fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d love to hear from you! What topics would you like me to cover in 2026? Are there specific technologies, patterns, or problems you&amp;rsquo;d like to see explored?&lt;/p&gt;
&lt;p&gt;Drop a comment below, reach out on social media, or send me a message. Your feedback helps shape the content direction for the year ahead.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s to a fantastic 2026 filled with learning, building, and sharing! 🚀&lt;/p&gt;</description></item></channel></rss>