<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Mike Street's Blog &amp; Notes</title>
		<link>https://www.mikestreety.co.uk</link>
		<description>Blog posts, notes and links from Mike Street (mikestreety.co.uk)</description>
		<language>en-gb</language>
		<pubDate>Thu, 12 Mar 2026 08:19:08 GMT</pubDate>
		<lastBuildDate>Thu, 12 Mar 2026 08:19:08 GMT</lastBuildDate>
		<atom:link href="https://www.mikestreety.co.uk/rss-all.xml" rel="alternate" type="application/xml" />
		<image>
			<url>https://www.mikestreety.co.uk/assets/img/favicon-512.png</url>
			<title>Mike Street's Blog &amp; Notes</title>
			<link>https://www.mikestreety.co.uk</link>
			<width>144</width>
			<height>144</height>
			<description>Lead Developer and CTO</description>
		</image>
		
		
		<item>
			<title>Completely remove DDEV from your computer</title>
			<link>https://www.mikestreety.co.uk/blog/completely-remove-ddev-from-your-computer/</link>
			<pubDate>Fri, 06 Feb 2026 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/completely-remove-ddev-from-your-computer/</guid>
			<description><![CDATA[
<p>I recently ran into an issue with DDEV 1.25 and was upgrading and downgrading between the two versions to test, check and verify.</p>
<p>Eventually, my DDEV got confused and started producing 404s. With mixed version images &amp; config, I wanted to remove everything and start again.</p>
<p>With the help of Claude, I created a bash script which will run through and delete every DDEV related configuration.</p>
<p><a href="https://gist.github.com/mikestreety/07d531b346ab8ce9c62ec655dd4274a4" class="button">Completely remove DDEV</a></p>
<h2>Steps</h2>
<ol>
<li>Copy the contents or download the zip</li>
<li>Make the file executeable - <code>cd path/to/file</code> and <code>chmod +x ./remove-ddev.sh</code></li>
<li>Run the script <code>./remove-ddev.sh</code> - there is <code>--help</code> and <code>--dry-run</code> flags available</li>
</ol>


<p><strong>Read time:</strong> 1 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Playwriter - Browser Automation MCP</title>
			<link>https://www.mikestreety.co.uk/notes/playwriter-browser-automation-mcp/</link>
			<pubDate>Tue, 06 Jan 2026 15:53:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/notes/playwriter-browser-automation-mcp/</guid>
			<description><![CDATA[
<p>Like Playwright MCP but via extension. 90% less context window. 10x more capable (full playwright API)</p>

<p><a href="https://github.com/remorses/playwriter">https://github.com/remorses/playwriter</a></p>
<p><strong>Read time:</strong> 1 mins</p>

]]></description>
		</item>
		
		
		<item>
			<title>2025 In Review</title>
			<link>https://www.mikestreety.co.uk/blog/2025-in-review/</link>
			<pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/2025-in-review/</guid>
			<description><![CDATA[
<p>Like 2024, 2025 passed without major incident or upheaval. All in all, it was an enjoyable year - seeing some firsts for us all.</p>
<h2>Life</h2>
<p>Plenty of goings-on with the Street family this year. Ruby, our youngest, started school which meant another shift in routines and schedules. Alfie moved up to Beavers and Ruby started Squirrels, which now means I'm the only one of our family to not be currently invested in a scouting section.</p>
<p>Alfie has become a classic &quot;kid&quot; and discovered Minecraft. He'd been talking about it at school and we finally gave in and bought a copy for the XBOX. I've also enjoyed playing it, trying to actually build things and thinking about layout (although it's still enjoyable to build a tower of TNT and blog it up).</p>
<p>There were some small home improvements - I redecorated the garden office and painted the &quot;TV corner&quot; of the lounge with matching bookcases. The biggest change was the demolition of the back garden patio and building of a deck - carried out by my dad and me.</p>
<p>To finish off the year, our car decided to give us a Christmas present of breaking. We couldn't get it booked in until the 5th Jan, however my mother-in-law leant us her car for the festive period which meant Christmas was saved.</p>
<h2>Trip and Holidays</h2>
<p>For our main holiday this year we took the kids to Disneyland Paris. It was a first for me - going on the channel tunnel and driving on foreign soil in my own car. Disneyland was hectic and expensive and fun and chaos. We went with my wife's family which did mean we had the opportunity to leave the kids with each other and head off to the big rides.</p>
<p>Little trips included taking Ruby to London for the first time, a boat trip out to the <a href="https://en.wikipedia.org/wiki/Rampion_Wind_Farm">Rampion Wind Farm</a> and a visit to Brooklands Museum in Weybridge.</p>
<p>We also spent a week in a static caravan in the New Forest - it was a classic &quot;caravan park&quot; style holiday, with on-site swimming, golf and evening entertainment. We sprinkled in some day trips to Paultons Park and a miniature steam railway. We also found a pub round the corner with an <em>incredible</em> outdoor play area for the kids (while Chilly and I kicked back with a book and a beer).</p>
<p>My attendance to gigs sky-rocketed this year as I got the taste for it last year. This year saw a few shows with the kids along with seeing OneRepublic (my first trip to the 02 since it was the millennium dome), Nizlopi (a birthday present), Self Esteem and it seems I can't go through a year without seeing Bastille. The last gig of the year was with my mum and family to see Stereophonics at the 02 again.</p>
<h2>Stats Analysis</h2>
<p><a href="/stats/">Visit the stats page</a>.</p>
<p>Cycling was a big part this year - recording the highest ever number of miles done in a year since I started recording. A big part of this was the turbo trainer I purchased at the end of last year, with just 85 miles separating virtual and real-world bike rides (and I rode more virtual miles then I did on an eBike)!</p>
<p>I also hit a few big rides this year, doing the London to Brighton bike ride (for the third time), a 65 mile bike ride in August and 70 miles around the Isel of Wight in October. I was pretty happy that I was able to pull these out the bag without <em>much</em> concious training. It seems cycling to and from work combined with the turbo proved to be a pretty effective training plan. I'd like to hit <strong>4000 miles</strong> again in 2026.</p>
<p>My Geocaching suffered this year - only finding 2 at the beginning of the year. I need to get some caching days booked in in 2026 to get those numbers back up. I feel like <strong>75 Geocaches</strong> is a good target.</p>
<p>Everything else, such as blog posts, steps and music streams stayed steady.</p>


<p><strong>Read time:</strong> 3 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Oh My Posh</title>
			<link>https://www.mikestreety.co.uk/notes/oh-my-posh/</link>
			<pubDate>Tue, 30 Dec 2025 08:36:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/notes/oh-my-posh/</guid>
			<description><![CDATA[
<p>The most customizable and fastest prompt engine for any shell</p>

<p><a href="https://ohmyposh.dev/">https://ohmyposh.dev/</a></p>
<p><strong>Read time:</strong> 1 mins</p>

]]></description>
		</item>
		
		
		<item>
			<title>Makefile includes and other Makefile tips and tricks</title>
			<link>https://www.mikestreety.co.uk/blog/makefile-includes-and-other-makefile-tips-and-tricks/</link>
			<pubDate>Sun, 28 Dec 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/makefile-includes-and-other-makefile-tips-and-tricks/</guid>
			<description><![CDATA[
<p>We use Makefiles in our repositories to wrangle commands for setting up, pulling and linting our code. Nothing revolutionary, but they've become pretty essential to our workflow.</p>
<h2>Makefile includes</h2>
<p>The thing that initially had me scratching my head was wanting to share commands between sites. A lot of our sites follow the same patterns and need identical Makefile commands - seemed daft to copy-paste the same stuff everywhere when we could have one central file doing the heavy lifting.</p>
<p>I spent ages trawling through Stack Overflow and various forums, but here's the thing - because our Makefiles essentially just contain bash commands (rather than actually &quot;making&quot; anything in the traditional sense), there was loads of conflicting advice that didn't quite fit our use case. Eventually, I gave up being stubborn and asked AI, which promptly gave me exactly what I needed:</p>
<pre class="language-makefile"><code class="language-makefile"><span class="token keyword">-include</span> ./path/to/file.mk</code></pre>
<p><strong>Word of warning:</strong> That hyphen <code>-</code> before <code>include</code> is doing important work - it prevents Make from throwing a tantrum if the file happens to be missing (which can happen if it's installed via a dependency that hasn't been pulled yet).</p>
<p><code>.mk</code> is the recognised file extension for Makefiles that aren't called <code>Makefile</code> - bit of trivia for you there.</p>
<p>When you're including a file with commands, you can overwrite them in your local Makefile if you need project-specific tweaks. Make will give you a gentle warning about this, but it's just letting you know something's been overridden - nothing to worry about.</p>
<h2>Variables</h2>
<p>Your central Makefile might need some paths or other bits that vary between projects. You can handle this much like bash - define variables without a prefix, use them with one:</p>
<pre class="language-makefile"><code class="language-makefile">SITE_PATH_PRODUCTION <span class="token operator">:=</span> ~/www/current

<span class="token keyword">-include</span> ./path/to/file.mk</code></pre>
<p>Then reference that variable in your shared Makefile:</p>
<pre class="language-makefile"><code class="language-makefile"><span class="token comment">## Pull the full database from production</span>
<span class="token target symbol">db-full-pull-production</span><span class="token punctuation">:</span>
	ssh <span class="token variable">$</span><span class="token punctuation">(</span>SSH_HOST<span class="token punctuation">)</span> \
<span class="token target symbol">		"<span class="token variable">$</span>(SITE_PATH_PRODUCTION)/vendor/bin/typo3 database</span><span class="token punctuation">:</span><span class="token keyword">export</span> ...</code></pre>
<p>As a bit of a safety net, you can set defaults at the top of your shared makefile too:</p>
<pre class="language-makefile"><code class="language-makefile">SITE_PATH_PRODUCTION <span class="token operator">?=</span> ~/www/current</code></pre>
<p>That way things won't explode if someone forgets to set a variable.</p>
<h2>.PHONY Commands</h2>
<p>All our commands are basically bash scripts masquerading as Make targets. This works fine until you accidentally create a folder that matches one of your command names.</p>
<p>For example, if you've got <code>make config</code> but also have a <code>config/</code> folder hanging about, running <code>make config</code> will target the folder instead of your command. Bit annoying when you're expecting it to do something entirely different.</p>
<p>You can tell Make which commands are <code>.PHONY</code> (i.e., they don't correspond to actual files), but since <em>all</em> of ours are just bash commands in disguise, we take the sledgehammer approach and mark everything as phony:</p>
<pre class="language-makefile"><code class="language-makefile"><span class="token builtin-target builtin">.PHONY</span><span class="token punctuation">:</span> *</code></pre>
<h2>Help block</h2>
<p>Here's something that'll save you from accidentally running the wrong command: by default, running <code>make</code> with no target executes whatever's first in the file. This could be anything - a rebuild command, a deployment script, something that downloads half the internet. Not ideal.</p>
<p>We stick a help generator at the top of our Makefiles that serves double duty - it shows available commands <em>and</em> acts as a safety net:</p>
<pre class="language-makefile"><code class="language-makefile"><span class="token target symbol">help</span><span class="token punctuation">:</span>
	<span class="token operator">@</span>echo <span class="token string">"\033[0;33mAvailable targets\033[0m"</span>
	<span class="token operator">@</span>echo <span class="token string">"\033[0;33m-----------------\033[0m"</span>
	<span class="token operator">@</span>awk <span class="token string">'/^[[:alnum:]_-]+:/ { \
		helpMessage = match(lastLine, /^## (.*)/); \
		if (helpMessage) { \
			helpCommand = substr($$1, 0, index($$1, ":")-1); \
			helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
			printf "%-25s %s\n", helpCommand, helpMessage; \
		} \
	} \
	{ lastLine = $$0 }'</span> <span class="token variable">$</span><span class="token punctuation">(</span>MAKEFILE_LIST<span class="token punctuation">)</span></code></pre>
<p>Any command with a double hash comment (<code>##</code>) above it gets picked up and displayed in the help. Simple but effective - and it means accidentally running <code>make</code> just shows you what's available rather than doing something potentially destructive.</p>
<p>Certainly not the most groundbreaking setup, but it's made managing our various projects much less of a faff. If you've got a different approach or improvements to suggest, I'd love to hear about them.</p>


<p><strong>Read time:</strong> 3 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>2025 Quiz of the Year</title>
			<link>https://www.mikestreety.co.uk/blog/2025-quiz-of-the-year/</link>
			<pubDate>Sat, 27 Dec 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/2025-quiz-of-the-year/</guid>
			<description><![CDATA[
<p>This years <a href="category/quiz/">quiz of the year</a> needs the following:</p>
<ul>
<li>The slides (linked below)</li>
<li>The info and notes below</li>
<li>Pens and paper for your teams</li>
</ul>
<p>The quiz can be played in teams or individually - I'll leave it to you to work it out. There doesn't need to be a &quot;quiz master&quot; per say, just someone who can click &quot;next slide please&quot;.</p>
<h2>Slides</h2>
<p><a href="https://docs.google.com/presentation/d/1iHsGoROP03uZz5BUKbMiLn9i4Tk7WISxWbR-ZgkYClY/edit?usp=sharing" class="button">Get the quiz slides</a></p>
<p>The slides are on Google, however if you need them in a different format, <a href="/contact/">let me know</a>.</p>
<h2>Quiz Information</h2>
<p>This quiz is 5 rounds with 7 questions in most rounds (except the picture round, which has 11).</p>
<p>Most answers are 2 points per correct answer (allowing single points to be rewarded where deserved).</p>
<p>When running the quiz I ask that phones are put away - more for politeness than fear of cheating. I also make it clear that the answers in the quiz are always right - even if they are not. This way it is fair and should hopefully avoid arguments.</p>
<h3>Round explanations</h3>
<h4>1.Battlenips (and other parts)</h4>
<p>Identify the grid reference where the specified body part of object is.</p>
<h4>2. Music</h4>
<p>Fill in the missing lines of the song. Single points can be awarded if the teams are <em>nearly</em> right.</p>
<h4>3. Film</h4>
<p>Identify the films featuring my face</p>
<h4>4. Picture</h4>
<p>I got my 7 &amp; 4 year-olds to draw things from the garden and park. What are they?</p>
<h4>5. 2025</h4>
<p>7 questions about what happened in 2025.</p>
<h2>The end</h2>
<p>Let me know if you use this quiz and how you get on - was it to easy? to hard? to complicated?</p>


<p><strong>Read time:</strong> 1 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Keeping RustFS clear of old assets</title>
			<link>https://www.mikestreety.co.uk/blog/keeping-rustfs-clear-of-old-assets/</link>
			<pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/keeping-rustfs-clear-of-old-assets/</guid>
			<description><![CDATA[
<p>After having <a href="/blog/setting-up-rustfs-as-an-amazon-s3-replacement/">RustFS</a> running as our <a href="/blog/use-minio-to-cache-gitlab-containers-and-runners/">Gitlab CI cache</a> for a few weeks the server (as expected) filled up.</p>
<p>Since we're only using RustFS to cache build assets, we can safely bin the old ones without worry. We settled on a 14-day cut-off - bit arbitrary really, but it works. The worst that can happen is the application won't deploy without them, which means you'll have to re-run the entire pipeline if you're trying to deploy something that hasn't been built in a fortnight. Not ideal, but hardly the end of the world.</p>
<p>RustFS is comapitble with <code>mc</code> - the MinIo command so I started looking there but then ended up with a default linux command</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">find</span> /data/rustfs0/XXX <span class="token parameter variable">-mindepth</span> <span class="token number">1</span> <span class="token parameter variable">-type</span> f <span class="token parameter variable">-mtime</span> +14 <span class="token operator">|</span> <span class="token function">xargs</span> <span class="token function">rm</span></code></pre>
<div class="info"><strong>Note:</strong> Make sure you specify the path (<code>XXX</code> in the example above) to your bucket as RustFS stores configuration in <code>/data/rustfs0/.rustfs.sys</code> - I ended up deleting our user access by removing files older than 14 days in this folder</div>
<p>Once you have the command and are happy with it, add it to a crontab to run once a night.</p>
<p>To edit the crontab, run <code>crontab -e</code> and place the following at the bottom (this will run a 10pm every evening)</p>
<pre class="language-bash"><code class="language-bash"><span class="token number">0</span> <span class="token number">22</span> * * * <span class="token function">find</span> /data/rustfs0/gitlab-ci <span class="token parameter variable">-mindepth</span> <span class="token number">1</span> <span class="token parameter variable">-type</span> f <span class="token parameter variable">-mtime</span> +14 <span class="token operator">|</span> <span class="token function">xargs</span> <span class="token function">rm</span></code></pre>


<p><strong>Read time:</strong> 1 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Email authentication records to improve deliverability</title>
			<link>https://www.mikestreety.co.uk/blog/email-authentication-records-to-improve-deliverability/</link>
			<pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/email-authentication-records-to-improve-deliverability/</guid>
			<description><![CDATA[
<p>Sending emails in this mad world of spam is a tricky business. Spoofing and phishing are all too common, and email providers try to be smart to it, although sometimes at the detriment to honest and &quot;real&quot; emails.</p>
<p>If your website is sending emails at all (even to you for contact form responses), it is worth considering spending time to verify that you own the domain and you are allowed to send emails from it. Word of warning: this isn't going to be the most thrilling post, but it's one of those things that'll save you a proper headache down the line when your carefully crafted emails end up in spam folders.</p>
<p><strong>SPF</strong>, <strong>DKIM</strong> and <strong>DMARC</strong> records all help with this and below each one is explained as to what it does and how you set it up. I'll be honest - I found these records a bit bewildering at first, but once you've set them up a few times they become second nature. Think of them as your email's passport - proving you are who you say you are.</p>
<h2>Testing Tools</h2>
<p>Before we dive in, bookmark this:</p>
<ul>
<li><a href="https://www.mail-tester.com/">mail-tester</a> - Send an email to get real-world data (proper useful, this one - gives you a score out of 10 and tells you exactly what's wrong)</li>
</ul>
<h2>SPF Record</h2>
<ul>
<li><a href="https://mailtrap.io/blog/spf-records-explained/">SPF Records explained</a></li>
<li><a href="https://mxtoolbox.com/spf.aspx">SPF Tester</a></li>
</ul>
<p>SPF (Sender Policy Framework) is basically your domain saying &quot;these are the mail servers allowed to send email on my behalf&quot;. Without it, anyone could pretend to send emails from your domain - which is as dodgy as it sounds.</p>
<p>This is how the most common SPF record looks:</p>
<pre><code>v=spf1 a mx -all
</code></pre>
<p>Breaking this down: <code>v=spf1</code> is the version, <code>a</code> and <code>mx</code> mean your domain's A record and MX records are allowed to send mail, and <code>-all</code> means &quot;reject anything else&quot;. That last bit is important - it's like saying &quot;if it's not on the list, it's not coming in&quot;.</p>
<p>As an example, if your client uses <strong>Google Workspace</strong>, you'll need to add <code>include:_spf.google.com</code>. Same goes for services like Mailchimp:</p>
<pre><code>v=spf1 a mx include:_spf.google.com include:mailchimpapp.net -all
</code></pre>
<p>Most services which send emails on your behalf will have some documentation detailing what SPF Record you need.</p>
<h2>DMARC Record</h2>
<ul>
<li><a href="https://dmarcian.com/dmarc-record-wizard/">DMARC wizard</a> (weirdly satisfying to use, this one)</li>
</ul>
<p>DMARC (Domain-based Message Authentication, Reporting and Conformance) tells receiving mail servers what to do if your SPF or DKIM checks fail. It also sends you reports so you can see if someone's trying to spoof your domain - certainly interesting to see what's being attempted in the wild.</p>
<p>A good standard is something like the following:</p>
<ul>
<li>Target: <code>_dmarc.@</code></li>
<li>Type: <code>TXT</code></li>
<li>Record:</li>
</ul>
<pre><code>v=DMARC1; p=quarantine; rua=mailto:email@example.com; aspf=r;
</code></pre>
<p>Or if you want to be a bit stricter:</p>
<pre><code>v=DMARC1; p=quarantine; pct=100; aspf=s;
</code></pre>
<p>The <code>p=quarantine</code> means &quot;if this looks dodgy, put it in spam rather than rejecting it outright&quot;. You can use <code>p=reject</code> if you're feeling confident, but I'd recommend starting with quarantine until you're sure everything's configured properly.</p>
<h2>DKIM Record</h2>
<p>This can only be configured if the service you are using emits a DKIM signature or similar. CMS's, like TYPO3, does not include a DKIM header so make sure you know before you start.</p>
<p>DKIM (DomainKeys Identified Mail) adds a digital signature to your emails - like a wax seal on a letter proving it hasn't been tampered with. Your email service provider will generate the keys for you.</p>
<p>Check with the system for instructions - each provider does it slightly differently and they'll give you the specific DNS records to add.</p>
<h2>BIMI</h2>
<p>Right, this one's a bit fancy and optional, but if you want your logo to appear next to your emails in supported clients (Gmail, Yahoo, etc.), BIMI is what you need.</p>
<ul>
<li><a href="https://bimigroup.org/bimi-generator/">BIMI Generator &amp; Inspector</a></li>
<li>Use a 512px square SVG for the image (the Favicon SVG is perfect for this)</li>
<li>We don't generally have a <strong>VMC</strong> (Verified Mark Certificate) available - these cost proper money and are only really worth it for big brands</li>
</ul>
<p>Example BIMI:</p>
<ul>
<li>Target: <code>default._bimi.@</code></li>
<li>Type: <code>TXT</code></li>
<li>Record: <code>v=BIMI1; l=https://link/to/svg;</code></li>
</ul>
<h2>Final Thoughts</h2>
<p>I should really test email deliverability more systematically on projects, but these DNS records are a good foundation. Set them up early and you'll avoid that awkward conversation later where the client asks why their contact form emails keep ending up in spam.</p>
<p>If anyone's got experience with VMC certificates for BIMI or has tips on DKIM implementation in TYPO3, I'd love to hear your thoughts. There's still a lot of nuance to email deliverability that catches me out occasionally.</p>


<p><strong>Read time:</strong> 3 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Setting up RustFS as an Amazon S3 replacement</title>
			<link>https://www.mikestreety.co.uk/blog/setting-up-rustfs-as-an-amazon-s3-replacement/</link>
			<pubDate>Sun, 12 Oct 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/setting-up-rustfs-as-an-amazon-s3-replacement/</guid>
			<description><![CDATA[
<p>I was at <a href="https://t3cl25.typo3.com/">TYPO3 Camp London</a> recently when Martin Helmich casually dropped that <a href="https://rustfs.com/en/">RustFS</a> was a solid MinIO replacement. That got my attention.</p>
<p>I've written about MinIO before - <a href="/blog/how-i-improved-the-speed-of-docker-builds-in-gitlab-ci/">speeding up Gitlab CI</a> and <a href="/blog/use-minio-to-cache-gitlab-containers-and-runners/">caching Gitlab assets</a> - and it's been great as a self-hosted S3 alternative. But I'm always up for trying new toys, especially when they promise improvements.</p>
<p>Turns out RustFS has been benchmarked against MinIO and is <a href="https://github.com/orgs/rustfs/discussions/598#discussion-8952907">faster across the board</a>. That was enough to convince me to give it a go.</p>
<h2>Server setup</h2>
<p>We're running RustFS on a dedicated Ubuntu server with <a href="https://www.hetzner.com/">Hetzner</a> (our go-to VPS provider). I went with an Intel CX32:</p>
<ul>
<li>4 vCPU</li>
<li>8 GB RAM</li>
<li>80 GB Disk</li>
</ul>
<p><strong>Note:</strong> You'll need an external IPv4 address - <code>rustfs.com</code> only supports IPv4 for setup.</p>
<h2>Installation</h2>
<p>Once your VPS is up, installation is pleasantly straightforward. First, make sure you've got <code>unzip</code>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">apt</span> update <span class="token operator">&amp;&amp;</span> <span class="token function">apt</span> upgrade
<span class="token function">apt</span> <span class="token function">install</span> <span class="token function">zip</span> <span class="token function">unzip</span></code></pre>
<p>Then grab the <a href="https://rustfs.com/en/download/?platform=linux">install script</a>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-O</span> https://rustfs.com/install_rustfs.sh <span class="token operator">&amp;&amp;</span> <span class="token function">bash</span> install_rustfs.sh</code></pre>
<p>Follow the CLI prompts and you're sorted.</p>
<h2>Setup</h2>
<p>After installation, hit up the web interface at <code>http://[server-ip]:9000</code>. Default credentials are <code>rustfsadmin</code> for both username and password.</p>
<p>Change that immediately by editing:</p>
<pre><code>/etc/default/rustfs
</code></pre>
<p>Once logged in, you can create buckets and extra users or access keys - which is what I've been using for Gitlab CI.</p>


<p><strong>Read time:</strong> 1 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Shower thoughts that live in my head rent free</title>
			<link>https://www.mikestreety.co.uk/blog/shower-thoughts-that-live-in-my-head-rent-free/</link>
			<pubDate>Sat, 11 Oct 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/shower-thoughts-that-live-in-my-head-rent-free/</guid>
			<description><![CDATA[
<p>The idea of shower thoughts are things for you to ponder or wonder, these are ones that I have read on the internet (I take no credit) and often think about for no reason at all.</p>
<blockquote>
<p>Sleep is one of the few things we pretend to do to actually do it</p>
</blockquote>
<p>Close your eyes, lie down and pretend until you actually drift off</p>
<blockquote>
<p>Cleaning your teeth is the only time we clean our skeleton</p>
</blockquote>
<p>You don't see a skull with a beard, do you?</p>
<blockquote>
<p>Why do we have round lenses on cameras, but rectangle sensors and photos?</p>
</blockquote>
<p>Surely the lens is capturing more of the photo then we ever see?</p>
<blockquote>
<p>Have you ever walked in a &quot;space&quot; of the earth that no human has before?</p>
</blockquote>
<p>Doesn't matter what is actually under your feet, but has a human ever been to that lat/long before?</p>


<p><strong>Read time:</strong> 1 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Migrate your GitLab instance to a new domain</title>
			<link>https://www.mikestreety.co.uk/blog/migrate-your-gitlab-instance-to-a-new-domain/</link>
			<pubDate>Fri, 10 Oct 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/migrate-your-gitlab-instance-to-a-new-domain/</guid>
			<description><![CDATA[
<p>We were sunsetting a domain and wanted to migrate our GitLab instance to a different URL.</p>
<p>GitLab doesn't make this easy. There's no big &quot;Change Domain&quot; button, and if you're using package registries, you're in for a proper adventure and need to get your team on board.</p>
<h2>Steps we'll cover</h2>
<ol>
<li>Add the new domain alongside the old one (30 mins)</li>
<li>Switch the primary domain (30 mins + testing time)</li>
<li>Set up redirects (1 hour)</li>
<li>Clean up (10 mins)</li>
<li>Deal with package registry authentication (varies, but budget a day if you're unlucky)</li>
</ol>
<p>In the examples below, I'm using:</p>
<ul>
<li><code>old.gitlab-company.org</code> - the old domain</li>
<li><code>new.gitlab-instance.com</code> - where we're migrating to</li>
</ul>
<p>Word of warning: if you're using GitLab as a package registry (NPM, Docker, whatever), prepare yourself. This is where most of the pain lives.</p>
<h2>Add the second domain</h2>
<p>The first step is to allow GitLab to accept connections from the new domain. This lets you test everything works before you commit to the switch - you'll be able to access GitLab on both domains simultaneously, which is brilliant for testing.</p>
<p>Once you point the domain record to your GitLab instance, you'll find you can navigate to it and click around - GitLab doesn't try to redirect you back to the primary domain. You may, however, encounter an SSL error. This can be resolved by adding the secondary domain to Let's Encrypt and allowing GitLab to generate an SSL certificate for it.</p>
<p>Edit the GitLab config file <code>/etc/gitlab/gitlab.rb</code> and add the following:</p>
<pre class="language-ruby"><code class="language-ruby">letsencrypt<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'alt_names'</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'new.gitlab-instance.com'</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">'registry.new.gitlab-instance.com'</span></span><span class="token punctuation">]</span></code></pre>
<p>Note: If you have a container registry or any other subdomains, these will need to be added too.</p>
<p>Reconfigure the instance:</p>
<pre class="language-ruby"><code class="language-ruby">gitlab<span class="token operator">-</span>ctl reconfigure</code></pre>
<p>This will generate the SSL certificates while reconfiguring and will error if there are any subdomains it can't generate certificates for.</p>
<p>I'd encourage using this new domain for a day or two (we left it longer, because paranoia, but two days is probably fine if you're braver than us). Navigate around, clone some projects, generally kick the tyres to make sure there are no basic issues.</p>
<h2>Switch the instance domain</h2>
<p>The next step is to change the instance URL. This won't force a redirect but will mean GitLab responds with the new URL for API and internal requests. You'll still be able to navigate on the old URL, clone projects, and so on.</p>
<p>Edit the GitLab config file <code>/etc/gitlab/gitlab.rb</code> and update the <code>external_url</code> and <code>letsencrypt['alt_names']</code>:</p>
<pre class="language-ruby"><code class="language-ruby">external_url <span class="token string-literal"><span class="token string">'https://new.gitlab-instance.com'</span></span>
registry_external_url <span class="token string-literal"><span class="token string">'https://registry.new.gitlab-instance.com'</span></span>
letsencrypt<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'alt_names'</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'old.gitlab-company.org'</span></span><span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">'registry.old.gitlab-company.org'</span></span><span class="token punctuation">]</span></code></pre>
<p>Reconfigure the GitLab instance:</p>
<pre class="language-ruby"><code class="language-ruby">gitlab<span class="token operator">-</span>ctl reconfigure</code></pre>
<p>I'd advise using this new domain with GitLab for a week or two. It won't redirect you to the new domain, but clone URLs and other requests will use the new domain.</p>
<h2>Package registries</h2>
<p>This is where you'll begin to see issues. If you use your GitLab instance as a Docker or package registry, you'll need to ensure you've authenticated with all your package managers using the new domain.</p>
<p>If you use GitLab as your NPM registry, this will be the biggest pain. Every project needs re-authenticating with the new domain, and npm will absolutely refuse to install packages until you do. It'll update your <code>package-lock.json</code> happily enough, then leave you staring at authentication errors wondering what you've done to deserve this.</p>
<p>If you use something like <a href="https://docs.renovatebot.com/">Renovate</a>, this can help with migration, but it takes a lot of planning (and a lot of head scratching). Weirdly, dealing with this across multiple projects was more time-consuming than the actual GitLab migration.</p>
<p><a href="/contact/">Get in touch</a> if you need advice with your specific setup - I spent enough time Googling obscure package registry authentication that I might actually be able to help.</p>
<h2>Redirect to the new domain</h2>
<p>With the new instance battle-tested and working, it's time to set up a redirect. You may choose to do this when you switch the instance domain above, but I left it a week or two in case we needed to fall back to the old domain (which, to be fair, we didn't, but better safe than sorry).</p>
<p>Edit the GitLab config file <code>/etc/gitlab/gitlab.rb</code> and add the option to allow custom nginx configuration:</p>
<pre class="language-ruby"><code class="language-ruby">nginx<span class="token punctuation">[</span><span class="token string-literal"><span class="token string">'custom_nginx_config'</span></span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string-literal"><span class="token string">'include /etc/gitlab/nginx-extra.conf;'</span></span></code></pre>
<p>Next, create a config file - <code>/etc/gitlab/nginx-extra.conf</code>. I chose not to redirect the registry, but if you need to, there's an example in <a href="https://robinopletal.com/posts/gitlab-on-two-domains">Running GitLab simultaneously on two domains</a>.</p>
<p>Before you reconfigure the GitLab instance, ensure the SSL certificates are in the location specified below:</p>
<pre><code># web
server {
  listen 443 ssl http2;
  server_name old.gitlab-company.org;
  server_tokens off;

  ssl_certificate /etc/gitlab/ssl/old.gitlab-company.org.crt;
  ssl_certificate_key /etc/gitlab/ssl/old.gitlab-company.org.key;
  ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256';
  ssl_protocols TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_session_cache builtin:1000 shared:SSL:10m;
  ssl_session_timeout 5m;

  return 301 https://new.gitlab-instance.com$request_uri;
}
</code></pre>
<p>Reconfigure the GitLab instance:</p>
<pre class="language-bash"><code class="language-bash">gitlab-ctl reconfigure</code></pre>
<h2>Clean-up</h2>
<p>After some time (we left it a month, but we're cautious like that), you can tidy things up:</p>
<ul>
<li>Delete <code>/etc/gitlab/nginx-extra.conf</code></li>
<li>Remove <code>nginx['custom_nginx_config']</code> from <code>/etc/gitlab/gitlab.rb</code></li>
<li>Remove any references to the old domain in <code>/etc/gitlab/gitlab.rb</code></li>
<li>Delete any references from your browser history</li>
</ul>
<p>The whole migration took us about three weeks from start to finish, mostly because we were being cautious and dealing with the package registry nightmare. If you're not using registries heavily, you could probably knock this out in a few days.</p>
<p>There's still a lot I don't know about GitLab's internals (it's a proper beast of a system), but this process worked well for us. If you hit any snags or your setup is a bit different, <a href="/contact/">get in touch</a> - I might be able to point you in the right direction.</p>


<p><strong>Read time:</strong> 4 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Add a space to your OSX doc for organisation</title>
			<link>https://www.mikestreety.co.uk/blog/add-a-space-to-your-osx-doc-for-organisation/</link>
			<pubDate>Fri, 19 Sep 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/add-a-space-to-your-osx-doc-for-organisation/</guid>
			<description><![CDATA[
<p>When having apps in my dock, I like to have them seperated by &quot;category&quot; - e.g. browsers, web dev and productivity.</p>
<p>To add the spaces, you can run the following in terminal:</p>
<pre class="language-bash"><code class="language-bash">defaults <span class="token function">write</span> com.apple.dock persistent-apps -array-add <span class="token string">'{"tile-type"="spacer-tile";}'</span><span class="token punctuation">;</span> <span class="token function">killall</span> Dock</code></pre>
<p>This adds an &quot;empty&quot; app icon that can be dragged around (and removed) as you see fit</p>
<p><img src="/assets/img/content/add-space-to-dock/dock.png" alt="Screenshot of my dock"></p>


<p><strong>Read time:</strong> 1 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>Setting up a new Apple computer for web development</title>
			<link>https://www.mikestreety.co.uk/blog/setting-up-a-new-apple-computer-for-web-development/</link>
			<pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/blog/setting-up-a-new-apple-computer-for-web-development/</guid>
			<description><![CDATA[
<p>I've been fortunate enough to get a new computer for work. Rather than migrate my old one, I always take it as an opportunity to start afresh. I've honed the apps I work with, so I know what I want, but I use it as an excuse to really question if everything is the right thing. I also use it as a test-case for running through our documentation for new starters, in case I've missed anything.</p>
<p>This isn't a &quot;must follow&quot;, but thought I would share the apps and settings I have, should anyone wish to take inspiration. <a href="https://www.mikestreety.co.uk/blog/setting-up-a-new-apple-computer/">This isn't the first time I've done this</a>, but this post is more comprehensive than the post from 2022.</p>
<h2>Settings</h2>
<p><strong>🍎 -&gt; System Settings</strong></p>
<h3>Lock screen</h3>
<p>The first thing is to increase the screensaver and screen timeouts - a 2 minute default is far too short.</p>
<p>I set the following:</p>
<ul>
<li>Start Screen Saver when inactive: Never</li>
<li>Turn display off on battery when inactive: 30 minutes</li>
<li>Turn display off on power adapter when inactive: 30 minutes</li>
</ul>
<p>I tend to shut/lock my computer out of habit anyway, so if I want it open and on, I want it open and on.</p>
<h3>Desktop &amp; Dock</h3>
<ul>
<li>Minimised window animation: Scale Effect</li>
<li>Automatically hide and show the Dock ✅</li>
<li>Show suggested and recent apps in Dock: ❌</li>
<li>Show desktop: Only in Stage Manager on Click</li>
<li>Hot Corners
<ul>
<li>Bottom left: Show desktop</li>
</ul>
</li>
<li></li>
</ul>
<h3>Keyboard -&gt; Function Keys</h3>
<ul>
<li>Use F1, F2, etc. keys as standard function keys: ✅</li>
</ul>
<h3>Accessibility -&gt; Zoom</h3>
<p>Next I enable zooming - it's handy when screen sharing or showing someone in the office. It allows you to hold Control (by default) and &quot;scroll&quot; up and down to zoom in and out.</p>
<ul>
<li>Use scroll gesture with modifier keys to zoom: ✅</li>
<li>Advanced
<ul>
<li>Zoomed image moves: Continuously with pointer</li>
<li>Show zoomed image while screen sharing: ✅</li>
</ul>
</li>
</ul>
<h2>Updates</h2>
<p>Before I do anything else I make sure the computer is up-to-date with any system updates</p>
<p><strong>🍎 -&gt; System Settings -&gt; General -&gt; Software Updates</strong></p>
<h2>TouchID for sudo commands</h2>
<p>Having to enter your password while setting up your computer can be tiresome, following Nick Taylor's <a href="https://one-tip-a-week.beehiiv.com/p/one-tip-a-week-touchid-for-sudo-commands">One Tip a Week: TouchID for sudo commands</a>, you can use your finger for any admin commands &amp; settings (if you have TouchID)</p>
<p>Edit the file (enter your password one last time)</p>
<pre><code>sudo vi /etc/pam.d/sudo_local
</code></pre>
<p>The add the following (you can always copy <code>/etc/pam.d/sudo_local.template</code> first)</p>
<pre><code># sudo_local: local config file which survives system update and is included for sudo
# uncomment the following line to enable Touch ID for sudo
auth       sufficient     pam_tid.so
</code></pre>
<h2>Homebrew</h2>
<p>Homebrew is the package manager which makes <em>everything</em> better. It's a central place to install and update your applications</p>
<p><a href="https://brew.sh/">Install Homebrew</a>.</p>
<p>As an optional extra, I use <a href="https://corkmac.app/">Cork</a> as a GUI to Homebrew - can help with the maintenance and visualisation of your installed apps.</p>
<p>If I need to install an app, I tend to lean towards using Homebrew to help with the updates and to keep track of everything installed.</p>
<h2>Apps</h2>
<p>I've tried to break apps down by category, but these are the ones I tend to have. Apps which can be installed with Homebrew have a 🍺 emoji next to them &amp; their package name.</p>
<h3>Productivity</h3>
<ul>
<li>Chrome (🍺 <code>google-chrome</code>)</li>
<li>Firefox (🍺 <code>firefox</code>)</li>
<li>1Password (🍺 <code>1password</code>)</li>
<li>BitWarden (🍺 <code>bitwarden</code>)</li>
<li>ClickUp (🍺 <code>clickup</code>)</li>
<li>TickTick (🍺 <code>ticktick</code>)</li>
<li>Slack (🍺 <code>slack</code>)</li>
<li>Raycast (🍺 <code>raycast</code>)</li>
<li>Hyperkey (🍺 <code>hyperkey</code>)</li>
<li>Viscosity (🍺 <code>viscosity</code>)</li>
<li><a href="https://apps.apple.com/us/app/spark-classic-email-app/id1176895641?mt=12">Spark (Classic)</a></li>
</ul>
<h3>Development</h3>
<ul>
<li>Visual Studio Code (VSCode) (🍺 <code>visual-studio-code</code>)</li>
<li>Sequel Ace (🍺 <code>sequel-ace</code>)</li>
<li>Iterm2 (🍺 <code>iterm2</code>)</li>
<li>Git (🍺 <code>git</code>)</li>
<li>Composer  (🍺 <code>composer</code>)</li>
<li>Node (🍺 <code>node</code>)</li>
<li>NVM (🍺 <code>nvm</code>)</li>
<li>Orbstack (🍺 <code>orbstack docker</code>)</li>
<li>DDEV (🍺 <code>ddev/ddev/ddev</code>)
<ul>
<li><code>mkcert -install</code></li>
</ul>
</li>
</ul>
<h3>Other</h3>
<ul>
<li>AppCleaner (🍺 <code>appcleaner</code>)</li>
<li>Spotify (🍺 <code>spotify</code>)</li>
<li>Claude &amp; Claude code (🍺 <code>claude claude-code</code>)</li>
<li><a href="https://ohmyz.sh/">Oh My ZSH</a>
<ul>
<li>powerlevel10k (🍺 <code>powerlevel10k</code>)
<ul>
<li>Run <code>echo &quot;source $(brew --prefix)/share/powerlevel10k/powerlevel10k.zsh-theme&quot; &gt;&gt;~/.zshrc</code></li>
<li>Then add the following plugins: <code>plugins=(git ssh-agent)</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>Uninstall Apps</h2>
<p>Once AppCleaner is installed, I then uninstall</p>
<ul>
<li>Garageband</li>
<li>iMovie</li>
</ul>
<h2>Restart</h2>
<p>Make sure you restart your computer during the setup, as some apps and settings need a clear cache to work effectively.</p>
<h2>Configuration</h2>
<p>With the applications set up, it's time to start configuring them</p>
<h3>SSH Access</h3>
<p>If you have a previous computer (and access to it) you can copy the <code>~/.ssh</code> folder across to allow you access to all your SSH places (such as Github, servers etc). If not, you'll need to generate a new key:</p>
<pre class="language-bash"><code class="language-bash">ssh-keygen <span class="token parameter variable">-t</span> ed25519 <span class="token parameter variable">-C</span> <span class="token string">"your_email@example.com"</span></code></pre>
<details>
<summary>Git</summary>
<p>We have some sensible Git config options we enable globally:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> config <span class="token parameter variable">--global</span> init.defaultBranch main
<span class="token function">git</span> config <span class="token parameter variable">--global</span> merge.ff <span class="token boolean">false</span>
<span class="token function">git</span> config <span class="token parameter variable">--global</span> pull.ff <span class="token boolean">true</span>
<span class="token function">git</span> config <span class="token parameter variable">--global</span> pull.rebase <span class="token boolean">true</span>
<span class="token function">git</span> config <span class="token parameter variable">--global</span> fetch.prune <span class="token boolean">true</span></code></pre>
<p>And then you can configure your user config:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> config <span class="token parameter variable">--global</span> user.name <span class="token string">"Your Name"</span>
<span class="token function">git</span> config <span class="token parameter variable">--global</span> user.email <span class="token string">"name@domain.example"</span></code></pre>
</details>
<details>
<summary>Raycast</summary>
<p>I use RayCast as a Spotlight replacement. To do so, I disable Spotlight:</p>
<p><strong>🍎 -&gt; System Settings</strong></p>
<ul>
<li>Spotlight
<ul>
<li>Disable everything</li>
<li>Search Privacy
<ul>
<li>Click +</li>
<li>Select the root hard drive</li>
</ul>
</li>
</ul>
</li>
<li>Keyboard
<ul>
<li>Keyboard Shortcuts
<ul>
<li>Spotlight
<ul>
<li>Uncheck everything</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Open RayCast and it will run through an onboarding:</p>
<ul>
<li>Set the Hotkey as ⌘ + Space (what Spotlight was)</li>
<li>Grant access to
<ul>
<li>Calendar</li>
<li>Files</li>
<li>Accessibility</li>
</ul>
</li>
</ul>
<p>Once completed, open the settings &amp; go to extensions. This is where it really pays off to have Hyperkey installed as you can set shortcuts for apps.</p>
<p>E.g, for me, I have <code>Caps Lock + E</code> to open ITerm and <code>Caps Lock + C</code> to open the Clipboard history. Things to look at</p>
<ul>
<li>Clipboard history</li>
<li>Window Management</li>
<li><a href="https://one-tip-a-week.beehiiv.com/p/one-tip-a-week-raycast-s-auto-join-for-meetings">Auto-join Meetings</a></li>
</ul>
</details>
<details>
<summary>iTerm</summary>
<p>First, we need to set up our <a href="https://www.mikestreety.co.uk/blog/syntax-highlighting-and-other-enhancements-for-vim/">Vim config</a></p>
<p>Then edit the iTerm preferences:</p>
<ul>
<li>General
<ul>
<li>Startup
<ul>
<li>Window restoration policy: Ony Restore Hotkey Window
Selection</li>
</ul>
</li>
<li>❌ Clicking on a command selects it to restrict Find and Filter.</li>
</ul>
</li>
<li>Appearance
<ul>
<li>General
<ul>
<li>Theme: Minimal</li>
</ul>
</li>
</ul>
</li>
<li>Profiles
<ul>
<li>Colours
<ul>
<li>Modes: ❌ Use separate colours for light and dark mode</li>
<li>Color Preset: Tango Dark</li>
</ul>
</li>
<li>Text
<ul>
<li>Cursor: <code>|</code></li>
<li>Font
<ul>
<li>MesloLGSNF</li>
<li>Weight: Regular</li>
<li>Size: 14,</li>
<li>Letter spacing: 100</li>
<li>Line-height: 120</li>
</ul>
</li>
</ul>
</li>
<li>Window
<ul>
<li>New Windows: 235 columns by 40 rows</li>
<li>❌ Use transparency</li>
</ul>
</li>
<li>Terminal
<ul>
<li>Scrollback lines: ✅ Unlimited scrollback</li>
<li>Bell: ✅ Silence bell</li>
</ul>
</li>
</ul>
</li>
</ul>
</details>

<p><strong>Read time:</strong> 4 mins</p>
<p><strong>Tags:</strong></p>
]]></description>
		</item>
		
		
		<item>
			<title>jQuery 4.0.0 release candidate 1 released</title>
			<link>https://www.mikestreety.co.uk/notes/jquery-400-release-candidate-1-released/</link>
			<pubDate>Mon, 11 Aug 2025 19:48:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/notes/jquery-400-release-candidate-1-released/</guid>
			<description><![CDATA[
<p>Yes, we still use jQuery. It's great to see it's still getting updates and people are still passionate about it.</p>

<p><a href="https://blog.jquery.com/2025/08/11/jquery-4-0-0-release-candidate-1/">https://blog.jquery.com/2025/08/11/jquery-4-0-0-release-candidate-1/</a></p>
<p><strong>Read time:</strong> 1 mins</p>

]]></description>
		</item>
		
		
		<item>
			<title>Read That F*cking Code!</title>
			<link>https://www.mikestreety.co.uk/notes/read-that-fcking-code/</link>
			<pubDate>Tue, 05 Aug 2025 12:48:00 GMT</pubDate>
			<guid>https://www.mikestreety.co.uk/notes/read-that-fcking-code/</guid>
			<description><![CDATA[
<p>&quot;If you’d told me 2–3 years ago that in 2025, one of my top pieces of advice for the new generation of developers would be “read your code” (we’re not even talking about re-reading it)… I’m not sure I would’ve believed you&quot;</p>

<p><a href="https://etsd.tech/posts/rtfc/">https://etsd.tech/posts/rtfc/</a></p>
<p><strong>Read time:</strong> 1 mins</p>

]]></description>
		</item>
		

	</channel>
</rss>
