<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://marksimpson82.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://marksimpson82.github.io/blog/" rel="alternate" type="text/html" /><updated>2026-04-06T06:54:56+00:00</updated><id>https://marksimpson82.github.io/blog/feed.xml</id><title type="html">Mark’s Devblog</title><subtitle>Thoughts about development. Oh and games stuff, too.</subtitle><author><name>Mark Simpson</name></author><entry><title type="html">On Long COVID</title><link href="https://marksimpson82.github.io/blog/2026/03/19/on-long-covid.html" rel="alternate" type="text/html" title="On Long COVID" /><published>2026-03-19T00:00:00+00:00</published><updated>2026-03-19T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2026/03/19/on-long-covid</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2026/03/19/on-long-covid.html"><![CDATA[<p>I’ve been meaning to write something about long COVID for a while since I’m directly affected, but it’s difficult to summon the energy. I partly want to write this just so I’ve got a record of it. Long COVID is tumultuous and confounding. Sometimes you doubt yourself.</p>

<p>Before I start, here’s an amazingly on-the-money primer on how Chronic Fatigue Syndrome (CFS) is often (mis)perceived by the general public (The Armando Iannucci Shows is a largely forgotten gem):</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/BDhoM_vhIeo" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p>I should also mention that <a href="https://bmjopenrespres.bmj.com/content/11/1/e001907">long COVID encompasses multiple phenotypes</a>. By this, researchers find there are common sub-groups of long COVID patients who have one or more symptom clusters (e.g. Fatigue, Cardiovascular, Neurological).</p>

<p>The CFS ‘phenotype’ of long COVID closely tracks with CFS itself. Sufferers are often misunderstood or forgotten.</p>

<h2 id="deaths-are-down-but-covid-19-is-not-over">Deaths are down, but COVID-19 is not “over”</h2>
<p>The world’s collective leadership seems to be pretending that COVID is over, and we’ve all moved on.</p>

<p>Firstly, I will acknowledge that COVID is now endemic and no longer represents a major risk to the average person in terms of deaths and hospitalisations. The figures don’t lie in that respect.</p>

<p><img src="/blog/assets/images/2026/03/hospitalised-and-deaths-covid.png" alt="Historical hospitalisation and mortality rates for COVID-19 in England" /></p>

<p>There are several reasons for the drops in death rates:</p>
<ol>
  <li>Many at-risk/infirm people died early in the pandemic, and cannot die again (pithy, but you get the gist – I still miss <a href="https://www.youtube.com/watch?v=nXbEFTv9zr0">John Prine</a> and many other folks who we sadly lost)</li>
  <li>Rapidly improved treatment protocols for high-risk patients (e.g. drugs like <a href="https://www.gov.uk/government/news/world-first-coronavirus-treatment-approved-for-nhs-use-by-government">Dexamethasone</a> and IL-6 Inhibitors, antivirals like Paxlovid etc.)</li>
  <li>From mid-2021 onwards, vaccinations protect against hospitalisation and death (look at that Omicron wave in 2022 compared to 2020!)</li>
  <li>Prior COVID infections also protect against hospitalisation and death</li>
</ol>

<p><strong>Aside</strong>: Most people (falsely!) believe the Omicron variant of COVID was significantly less deadly compared to the original variant, but this is a misconception. Omicron was highly <em>transmissible</em> (it spread freely) and was just as <em>severe</em> as the <strong>original</strong> variant of COVID; it just didn’t feel like it because:</p>
<ol>
  <li>The Delta variant was much nastier</li>
  <li>The population was benefitting from (I sound like a COVID estate agent, sorry) some of the things I’d previously mentioned.</li>
</ol>

<p>It’s not all good news, though. We also know that, amongst other things:</p>
<ul>
  <li>Each successive COVID infection has a chance of causing long COVID</li>
  <li>All-cause mortality increases for a prolonged period after a person is infected with COVID</li>
  <li>COVID infections cause a reduction of grey matter in the brain</li>
</ul>

<p>We also know that so-called “long haulers” are in the millions in most countries. While most recover inside a year, many do not.</p>

<h2 id="my-problems-with-long-covid">My problems with long COVID</h2>
<p>I’ve been infected with COVID twice, and both infections resulted in long COVID with a range of symptoms.</p>

<h3 id="infection-one-april-2020">Infection one: April 2020</h3>
<p>I would’ve been 37 at the time. I weighed around 89 kg (196 lbs) at 6’6 (198 cm). I was pretty fit and strong, could hit a 30 kg weighted pull-up and a 50 kg weighted dip – hardly record-breaking stuff, but good going for me. Around that time, I ran 3 half-marathons in successive weeks for fun. This is all to say, I was fitter than the average Joe. While I’m sure being fit and healthy helped, it did not stop COVID doing a number on me.</p>

<p>I was well-briefed on COVID and was generally pretty careful. I prioritised ventilation, stopped taking public transport &amp; taxis and generally played it safe. I <em>thought</em> the gym was relatively safe, as it was quiet and the big door(tm) at the front was open. This was hubris – I should’ve been even more cautious.</p>

<p>Looking back, we didn’t know a great deal about COVID. The major broadcasters in the UK weren’t mentioning that losing your sense of taste and smell was a symptom. This was a new experience for me, so I did a bit of digging on the web and decided to self-isolate for two weeks. I’m glad I did because I was due to visit my family.</p>

<p>The bout of COVID itself was a non-event. It felt no worse than a cold, plus the aforementioned loss of taste and smell. I was back doing home gym stuff and running immediately afterwards with seemingly no ill effects. Easy.</p>

<h3 id="long-covid-one">Long COVID one</h3>
<p>A few weeks or perhaps a month later (I didn’t keep a diary, sadly), things took an unexpected turn: I was struck down by crippling fatigue. The exhaustion was so all-encompassing that I didn’t even register the headache – the two were entwined, inseparable and crushing.</p>

<p>The only way I can explain the feeling is that you’re a toddler that’s run a marathon, you’ve got the raging abdabs and you’re basically crabby as fuck – you will fall asleep at the drop of a hat. 50% of your cells are already asleep. 25% are in that dozing phase (where deep sleepers are nominally awake, but can have conversations they don’t remember) and the final 25% are technically awake, but in power saving mode and soon to shut down.</p>

<p>There was no known thing called “Long COVID” back then, so I was confused and perturbed in equal measure. My former flatmate had suffered from a prolonged bout of post-viral fatigue after contracting glandular fever, and that was the only thing I could think of that was comparable.</p>

<p>I would clamber out of bed in the mornings and ‘go’ to work (part-time, remote working as a software engineer), but found I couldn’t stay awake long enough to be productive. I was supposed to work two days a week as a temporary gig, but had to split my two days of work into four or five smaller sessions. Even two hours a day was a challenge – it took an age to complete simple tasks. I’d often wake up, ‘work’ for an hour, and then go back to bed, defeated.</p>

<p>Bed is good when you’re ill though, right? In a cruel twist of fate, bed was arguably worse. When lying down, there was a sensation of pressure on my chest. My upper chest ached. The moment I drifted off, I would wake up feeling like I’d not taken a breath for minutes. I didn’t know it at the time, but the symptoms were possibly pericarditis (inflammation of the heart lining) – more on this later.</p>

<p>Anyway, this went on for weeks, and then months. I don’t know how long exactly because, again, I didn’t keep a symptom diary. I saw the GP multiple times in June, so it likely tailed off after that and I recovered.</p>

<h3 id="recovery-one">Recovery one</h3>
<p>The problem with COVID, as we were beginning to find out, is that it’s not a simple respiratory virus. While most people shrug it off, a minority do not.</p>

<p>COVID makes it its business to get into everything. Lungs? Sure. But how about heart, stomach, bowels, brain and the works? It gets into your blood. It even trips up your vascular system.</p>

<p>I felt largely fine by day, but as the months dragged on I still had bouts of intermittent chest pressure/pain and woke up minutes after falling asleep, gasping. I’ve always been a deep sleeper and a non-worrier. I hit the pillow and it’s lights-out, so it was annoying.</p>

<p>Anyway, I noticed something new: heart palpitations. Several times a day, apropos of nothing, my heart would add extra beats. The sensation was very unpleasant: it was like being thumped in the chest. Each time, I’d wait to see if it’d keep on beating. “Unpleasant, but likely benign”, was my self-diagnosis and the GP wasn’t concerned – they had a lot of ground to cover.</p>

<p>I returned to exercise. I ran, I lifted weights. But when I raised my heart-rate, I noticed the palpitations increased in frequency. Exercise was now tied to negative physical effects.</p>

<p>This went on for years, and I finally got a 24 hour heart monitor in 2023 that allowed a cardiologist to diagnose me with intermittent first-degree heart block (along with a bunch of other scary-sounding but ultimately benign conditions). COVID likely was to blame.</p>

<p>As I previously mentioned, myocardial infarctions (heart attacks) occur at an elevated rate in people who’ve recently had COVID. I didn’t have a heart attack, but it’s yet another thing that COVID can cause, and it shows up in every country’s catch-all excess mortality figures.</p>

<h3 id="life-from-2021-2024">Life from 2021-2024</h3>
<p>A lot happened in this period. I quit eeGeo (aka WRLD) – a company that I’d worked for from 2010-2022 – and joined Infinity Works as a senior consultant. This meant travel and in-person working was back on the cards.</p>

<p>After my first bout with long COVID, I took precautions:</p>
<ul>
  <li>I insisted on eating and drinking outside (weather permitting!)</li>
  <li>I got all of my COVID vaccinations/boosters</li>
  <li>I purchased a large supply of N99/FFP3 face masks and wore them when shopping, using public transport, visiting the doctor and for other things I saw as ‘utilitarian’</li>
  <li>I bought an <a href="https://aranet.com/en/home/products/aranet4-home">Aranet4 air quality meter</a> to gauge risk when at work and in unfamiliar client offices (highly recommended).</li>
</ul>

<p>I got some funny looks. As of early 2022 COVID was deemed ‘over’ and a chunk of the population viewed masking as an unpleasant reminder that COVID still existed.</p>

<p>There’s a certain group who assume that because COVID didn’t harm <em>them</em>, being careful with masking / air quality to protect yourself is simply high theatre – a twatty affectation. The calculus changes after you’ve spent months being unable to stay awake, only to wake up, gasping, with an elephant on your chest.</p>

<p>Some nutter tried to remove my mask as I walked out of the shops and was summarily told to go fuck himself. He ranted at me, telling me that “people like me” were the reason his children were home from school. These are the toss-arses you see on Facebook confidently talking about “vaccine injury” because Bev from 3 doors down “definitely knows someone high up at the hospital”, and “it’s all a big cover-up, yeah?”</p>

<p>On the more mundane end of the spectrum, a few people in client offices made snarky remarks and treated me like I was crazy for wearing a mask in a crowded meeting room when the CO₂ hit 1200ppm and continued increasing. Someone said, “you know masks don’t work, right?”, as he thought my N99 mask was a cloth one or had no idea of the differences. No point arguing, though. In my experience, people get over it when you stick to your guns and quietly get on with it.</p>

<p>I didn’t wear a mask 24/7 mind, e.g. I didn’t mask in open-plan areas at work where CO₂ was at reasonable levels.</p>

<p>So anyway, I’d had some unpleasant months and my quality of life was slightly diminished after the heart debacle, but life goes on. Until…</p>

<h3 id="infection-two-september-2024">Infection two: September 2024</h3>
<p>Time flies. I was 41. I’m pretty sure I picked up the COVID infection on the train back from Edinburgh. I’d been on a whisky-tasting tour with a friend and succumbed to pint-mania after an excellent day out. At least I got infected doing something fun rather than buying reduced price fishcakes in Lidl.</p>

<p>On the train back, I broke my rule: I was half-cut and dozed off without putting on my mask. I was rudely woken 20 minutes later by a rude berk with a barking, hacking cough. They were clearly ill but travelled without a mask. I put on my mask and moved carriage but alas, too little, too late.</p>

<p>Unlike my first go around, this COVID infection was hellish. It was like proper flu. If you’ve ever had the proper flu (where you are bed-bound, sweating yet freezing, coughing to the point where your throat is raw and feeling like you’ve been swaddled in a sleeping bag and summarily beaten with hammers), you’ll know what I mean. It lasted two weeks.</p>

<p>Just as suddenly as it struck me down, the clouds cleared. I felt tired and weak after weeks of indolence and eating much of nothing, but not too bad considering.</p>

<p>This time around, I knew there were recommendations for e.g. athletes to take it easy after a COVID infection; I decided gentle walks would suffice, at least until I’d waited three weeks or so. Just as with my first COVID infection, there was a short period of good health afterwards.</p>

<h3 id="non-recovery-two">Non-Recovery two</h3>
<p>Some sequels are occasionally better than the original (The Dark Knight, The Bourne Ultimatum, arguably Terminator 2). This one was straight to DVD, starring a rotund and panting Steven Seagal.</p>

<h4 id="symptoms">Symptoms</h4>
<p>Three to four weeks in, a creeping tiredness set in. I’d started doing some (very!) light jogging at lunchtime and figured I needed to shake off the rust. The round trip to my sandwich shop of choice is roughly 3.5k (around 2.2 miles). I decided to jog downhill and walk back.</p>

<p>Over the course of the week, crushing fatigue duly pressed me into the ground; each day got progressively harder. The chest pain returned. A headache joined the party and never left – my eyes feel like they’re too big for my skull, my sinuses hurt and there’s a band of pain across my forehead. Once again, the headache and the fatigue are usually entwined, one, all-consuming. Worse still, this time sleep was not refreshing – I’d open my eyes in the morning and feel as tired as the previous night. This made the fatigue relentless.</p>

<p>I’ve twice had my eyes tested at the optician to rule out eye-strain as a contributing factor. The dentist is pretty confident that my unerupted wisdom teeth are not causing referred pain, as such pain is rarely bilateral. A brain MRI came back showing that, yes, I have a brain and no, there’s nothing sinister on the scan. Last time I took my blood pressure, it was bang-on 120/60.</p>

<p>The annoying thing with long COVID is that many laboratory and diagnostic tests look normal. Doctors may doubt you (luckily, mine has been supportive). You may even doubt yourself at times. Sometimes I wonder if I just need to pull myself together, but a few hours or days later I am forcefully disavowed of those doubts as I struggle to decant myself from bed. We go round again.</p>

<h4 id="fatigue-fatigue-fatigue">Fatigue, Fatigue, Fatigue</h4>
<p>Here’s where long COVID confounds you. Many people with long COVID <em>can</em> do things in the moment. But, like buying <em>Big TV</em> on tick or drinking 4 pints in an hour, you will pay for it down the line.</p>

<p>I liken this to Wile E. Coyote from <em>The Road Runner</em>: he runs out over the cliff and stands in thin air. Gravity is paused for a little while, but not long.</p>

<p><img src="/blog/assets/images/2026/03/wile-e-coyote.png" alt="Wile E. Coyote hanging in mid-air" /></p>

<p>It’s also a bit like having a malfunctioning battery with no charge level shown. Sometimes the capacity is as expected. Other times, it is flat and you’re unknowingly running on fumes.</p>

<p>Each CFS sufferer has to mind their energy levels on multiple fronts, including:</p>
<ul>
  <li>Physical activity (exercise, walking, standing upright, even sitting upright in bed etc.)</li>
  <li>Cognitive effort (thinking, working, writing – this blog post is depleting my reserves as I type)</li>
  <li>Social (meeting friends, speaking to people)</li>
  <li>General sensory stimulus (being in a loud place, bright lights, etc.)</li>
</ul>

<p>The interplay between these aspects is unpredictable. Sometimes I’m fine, sometimes I do the same thing again and crash. Maybe things were different somehow? Maybe my energy levels were already depleted? Hard to say. As John Lydon once sang (before he started advertising butter), <a href="https://www.youtube.com/watch?v=NJZe_AVtD2c">“Don’t ask me - ‘cause I don’t know”</a></p>

<h4 id="symptom-tracking">Symptom Tracking</h4>
<p>For stat and symptom tracking, I bought myself a <a href="https://www.ebay.co.uk/sch/i.html?_nkw=fitbit+inspire+3">cheap FitBit Inspire 3</a> (you can pick these up refurbished for cheap on eBay - around £30) in early 2025 and also track my HRV/pulse with the free <a href="https://www.makevisible.com/">Visible app</a>. Combined, I can track things like:</p>
<ul>
  <li>Sleep quality and duration (Awake, Light Sleep, REM Sleep, Deep Sleep)</li>
  <li>Resting Heart Rate (RHR)</li>
  <li>Heart Rate Variability (HRV)</li>
</ul>

<p>I can recommend this combination because it can offer insight into your physical state – either warning you that something is off (RHR is high, HRV is low) when you’re unaware you should be taking it easy, or offering confirmation that yes, you are indeed feeling ‘off’. For example, I picked up a viral infection in January and …</p>

<p><img src="/blog/assets/images/2026/03/visible-hrv-rhr.jpg" alt="Visible app showing RHR/HRV changes" /></p>

<p>I have symptoms suggesting <a href="https://www.rdash.nhs.uk/services/long-covid/dysautonomia-in-long-covid/">dysautonomia </a>, too. Sometimes my heart will race when I stand or walk, but not always. I’ve had a heart-rate reading of 130 bpm after a quick shower. My heart-rate peaked at 158 bpm while walking up a hill a few weeks back. Sometimes gentle activity causes spikes. Other days it is normal. If you don’t track, you don’t know.</p>

<h4 id="quality-of-life">Quality of Life</h4>
<p>Here’s a graph from the <a href="https://en.wikipedia.org/wiki/Myalgic_encephalomyelitis/chronic_fatigue_syndrome#Illness_severity">ME/CFS Wiki Page</a> that tells a story (scroll down…):</p>

<p><img src="/blog/assets/images/2026/03/cfs-quality-of-life.png" alt="Health-Related Quality of Life" /></p>

<p>Misery is not a competition, but I want people to understand that dealing with chronic fatigue that permeates most areas of your life is generally a shite experience and I wouldn’t recommend it.</p>

<p>Everyone is affected differently. I’m lucky in that many basics I take for granted aren’t badly affected, e.g.:</p>
<ul>
  <li>Cooking</li>
  <li>Reading for half an hour</li>
  <li>Passively watching TV</li>
  <li>Playing familiar games</li>
  <li>Walking slowly (I try to keep the distances short and my heart-rate low)</li>
</ul>

<h4 id="how-people-perceive-cfs">How people perceive CFS</h4>
<p>Things can look normal to an observer. However, bear in mind that when you meet someone who has chronic fatigue or any hidden illness, you will likely be seeing them on a good day. If they were having a bad day, they’d be at home.</p>

<p>You do not see that person when they’re crashing, sleeping all hours and generally feeling terrible.</p>

<h4 id="some-positives-for-me-at-least">Some positives (for me, at least!)</h4>
<p>Firstly, I’m quite lucky compared to some. Many long haulers cannot get out of bed for very long (or at all), hold a conversation, read a book or smell the literal or metaphorical roses. They exist and not much more.</p>

<p>Fatigue comes in self-moderating waves. It’s self-moderating because when you’re down, you can’t do much of anything. I’ve had good weeks where I felt upbeat for the future and dared dream of working up to a light jog. But I’ve also had many bad weeks where I spent 16 hour stretches in bed feeling wretched. I’m writing this after spending two such days in bed and feeling pretty pissed off about it.</p>

<p>I’m not a stoic by any means. I frequently feel pretty bad about the whole thing, but then I bounce back, Partridge-style. A-Ha! And write annoying blog posts. A-Ha!</p>

<p>Finally, I likely responded to some treatments – though it’s hard to say for sure, as sometimes you just get better by coincidence and the treatment had no effect.</p>

<h4 id="treatments">Treatments</h4>
<p>There’s a lot of experimental treatments flying about and this is a whole other post to make, but in short:</p>
<ul>
  <li><a href="https://en.wikipedia.org/wiki/Low-dose_naltrexone">Low-dose Naltrexone</a> <a href="https://shop.dicksonchemist.co.uk/the-ldn-private-prescription-dept/">via Dickson Chemist</a>
    <ul>
      <li>For sleep and fatigue</li>
    </ul>
  </li>
  <li><a href="https://en.wikipedia.org/wiki/Amitriptyline">Amitriptyline</a> (via NHS), <a href="https://health4all.co.uk/product/vitamin-b2-riboflavin-400mg-capsules/">Vitamin B2, 400 mg</a> and Magnesium (370 mg from Lidl)
    <ul>
      <li>For headaches – this is a fairly standard migraine medication stack</li>
      <li>I unfortunately had to get referred to a neurologist to access this</li>
    </ul>
  </li>
  <li><a href="https://g-niib-uk.com/">G-NiiB (SIM01) Probiotics</a>
    <ul>
      <li>For fatigue. See <a href="https://www.thelancet.com/journals/laninf/article/PIIS1473-3099(23)00685-0/fulltext">this paper</a>, but no idea if it does anything just yet</li>
    </ul>
  </li>
</ul>

<p>For general wellbeing and mildly reducing depression: (this is the useful version of stuff you see plastered on the back of a bus, asking, <em>“Feeling Tired?”</em>, except you’re not getting rinsed for a useless multi-vitamin):</p>
<ul>
  <li><a href="https://nutravita.com/products/vitamin-d3-4000iu-softgel-capsules-400-days-supply">Vitamin D3</a> (4000 IU)
    <ul>
      <li>Vitamin D3 is more easily absorbed by the body</li>
    </ul>
  </li>
  <li><a href="https://nutravita.com/products/omega-3">Omega-3</a>
    <ul>
      <li>The recommendation I read was to go for EPA at around ~60% of the Omega-3 total, and ~40% DHA.</li>
      <li>Take between 1,000 and 1,500 mg of Omega-3 per day (2 or 3 of those caps does the trick, but you may want to start with one cap and ramp up over time)</li>
    </ul>
  </li>
</ul>

<p>There are many potential and promising treatments, but this post is long enough as it is. The main thing is to periodically review the literature and only plump for low-risk and accessible treatments. There are a lot of things I  <em>want</em> to try, but I can’t get access to them via the NHS, and there are no clinical trials in my area.</p>

<p>For me, the combination of LDN and Amitriptyline likely improved my sleep quality. I was around 10 months in when I started taking them, and sleep rapidly became productive and refreshing which was a godsend, as I was depressed and cracking up. My REM sleep metrics also improved a great deal, going from 40% of expected up to more like 80%.</p>

<h4 id="low-dose-naltrexone-dreaming">Low-dose Naltrexone Dreaming</h4>
<p>It’s not all misery. One of LDN’s side-effects is lucid (and often bizarre) dreams, of which I had many. This is proper season 6 of the <em>Sopranos</em> stuff.</p>

<p><strong>Dream</strong>: I walk home in my dream. People are asleep in shop doorways, pulling newspapers over them for warmth. Cyclists spit at me. My Welsh friend’s dog scoots under a bus and is turned into a furry pancake. He begs with me to borrow my phone, as he needs to alert the authorities, but I’m too busy studying a menu in a restaurant window to respond. Dean (the Welshman) stares at the menu, bewildered. “What’s a starter?”, he asks. “It’s like the opposite of a dessert”, I matter-of-factly reply. Also, Dean doesn’t have a dog IRL.</p>

<p><strong>Dream</strong>: I’m on an Eastern European rail holiday with the much-loved, but long-departed socialist <a href="https://en.wikipedia.org/wiki/Harry_Leslie_Smith">Harry-Leslie Smith</a>. I suspect HLS was a stand-in for Alexei Sayle, as I’d read his autobiography around that time (<a href="https://www.theguardian.com/books/2010/oct/16/stalin-ate-homework-alexei-sayle-review">“Stalin Ate My Homework”</a>) which featured a lot of this sort of thing, and very enjoyable it was too.</p>

<p><strong>Dream</strong>: Two of my friends have a dog. The dog goes missing. All friends and acquaintances are gathered and we’re told to systematically comb Dundee. We divide the larger area into parcels of land and each person gets cracking. I am told to search the (non-existent) railway station at Magdalen Green. I don’t bother. They don’t find the dog. I am wracked with guilt.</p>

<p><strong>Dream</strong>: I see a friend (I’ll call them ‘X’) on the street. They’ve been in seclusion. “Good to hear from you”, I said. We make plans to meet up. “Maybe we can invite X along, too?” (I’m asking him to invite himself for some reason!) “Oh no, he’s the reason I’ve been avoiding everyone”, X replies. We do not meet up.</p>

<p><strong>Dream</strong>: A grizzly bear is burgling my parents’ house and threatening us with violence. I try to smooth-talk the bear so that I can fetch our shotgun from an outbuilding and save the day. We don’t have a shotgun or an outbuilding.</p>

<p>This nonsense goes on for months, but I don’t mind it :-)</p>]]></content><author><name>Mark Simpson</name></author><category term="covid" /><category term="long_covid" /><summary type="html"><![CDATA[I’ve been meaning to write something about long COVID for a while since I’m directly affected, but it’s difficult to summon the energy. I partly want to write this just so I’ve got a record of it. Long COVID is tumultuous and confounding. Sometimes you doubt yourself.]]></summary></entry><entry><title type="html">Protecting SSH keys with BitWarden</title><link href="https://marksimpson82.github.io/blog/2025/12/22/bitwarden-vault-ssh-agent-keys.html" rel="alternate" type="text/html" title="Protecting SSH keys with BitWarden" /><published>2025-12-22T00:00:00+00:00</published><updated>2025-12-22T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/12/22/bitwarden-vault-ssh-agent-keys</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/12/22/bitwarden-vault-ssh-agent-keys.html"><![CDATA[<h2 id="lots-of-hacks-of-late-some-commonality">Lots of hacks of late, some commonality</h2>
<p>I’ve been reading a fair number of post-mortems of late due to the number of <code class="language-plaintext highlighter-rouge">npm</code> supply chain hacks. The hackers often run <a href="https://docs.npmjs.com/cli/v8/using-npm/scripts"><code class="language-plaintext highlighter-rouge">post-install</code> scripts</a> to harvest credentials from the victim’s machine. That is: once you’ve been pwned, the hacker will iterate through a number of directories that commonly store sensitive credentials (e.g. <code class="language-plaintext highlighter-rouge">~/.aws</code>, <code class="language-plaintext highlighter-rouge">~/.ssh</code>, etc.)</p>

<h2 id="by-default-ssh-keys-live-on-the-filesystem">By default, SSH keys live on the filesystem</h2>
<p>If you’ve ever set up a GitHub account or used ssh for anything, you’ll probably be aware of the fact that, by default, both your public <strong>and private</strong> SSH keys are stored on the filesystem, unencrypted.</p>

<p>Yup, <code class="language-plaintext highlighter-rouge">ls ~/.ssh/</code> and you’ll see what I mean.</p>

<h2 id="can-we-do-better">Can we do better?</h2>
<p>Yes. We can store SSH keys in a password manager such as <a href="https://bitwarden.com">BitWarden</a>. BitWarden has its own ssh agent built into its Desktop App (while BitWarden also offers a CLI, I’m not sure it offers SSH key integration like the desktop app).</p>

<p>Once enabled, an attacker cannot trivially hoover up SSH files from our <code class="language-plaintext highlighter-rouge">~/.ssh</code> directory because they no longer live there, in the clear.</p>

<p>The only downside is that you must run BitWarden desktop to enable the ssh keys. It adds a little bit of friction for a bit more security; I think it’s a reasonable tradeoff. If the BitWarden app is open but locked, using an SSH key will cause the app to flash.</p>

<p>If you’re not running BitWarden or fail to unlock the vault, you’ll see something like the following (which isn’t very user-friendly):</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># git auth error example when BitWarden not running / unlocked</span>
<span class="nv">$ </span>git pull
git@github.com: Permission denied <span class="o">(</span>publickey<span class="o">)</span><span class="nb">.</span>

<span class="c"># or something like</span>
<span class="nv">$ </span>ssh-add <span class="nt">-L</span>
Could not open a connection to your authentication agent.
</code></pre></div></div>

<h2 id="short-version-for-windows-users">Short version for Windows users</h2>
<ol>
  <li>Follow the official <a href="https://bitwarden.com/help/ssh-agent/">BitWarden tutorial steps</a></li>
  <li>However, it’ll fail when trying to test the SSH agent via <code class="language-plaintext highlighter-rouge">ssh-add -L</code></li>
  <li>Skip ahead to the tutorial step that details how to change the <code class="language-plaintext highlighter-rouge">core.sshCommand</code></li>
</ol>

<p>It should then work.</p>

<p>The <code class="language-plaintext highlighter-rouge">core.sshCommand</code> bit is currently tucked away under a git commit signing section later in the page, but this configuration step is needed to get the basic BitWarden ssh agent working when using git bash.</p>

<p>I’ve submitted feedback and a suggested fix to the page (on 2025-12-21), so hopefully they’ll re-organise the steps.</p>

<h2 id="longer-version">Longer version</h2>
<p>Again, this is for Windows. I don’t think this affects Mac/Linux users.</p>

<p>The <a href="https://bitwarden.com/help/ssh-agent/">BitWarden SSH documentation</a> has the instructions, but my opinion is the steps are a little out of order. Why? Because if you’re using git bash on Windows, you absolutely <strong>need</strong> to set the following option regardless of whether you’re signing git commits:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config <span class="nt">--global</span> core.sshCommand <span class="s2">"C:/Windows/System32/OpenSSH/ssh.exe"</span>
</code></pre></div></div>

<p>If you do not set this option, you’ll receive an error message even when BitWarden is running and you’ve entered your master password to unlock the vault:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Could not open a connection to your authentication agent.
</code></pre></div></div>

<p>This is because the default <code class="language-plaintext highlighter-rouge">ssh</code> / <code class="language-plaintext highlighter-rouge">ssh-add</code> executables for git bash on Windows are different from the Windows defaults, and BitWarden is hooking into the OpenSSH versions, not the Git Bash ones.</p>

<p>Let’s use a standard windows cmd prompt and see where our <code class="language-plaintext highlighter-rouge">ssh</code> / <code class="language-plaintext highlighter-rouge">ssh-add</code> binaries live:</p>

<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code># <span class="kd">windows</span> <span class="nb">cmd</span> <span class="o">--</span> <span class="kd">you</span> <span class="kd">can</span> <span class="kd">see</span> <span class="kd">the</span> <span class="kd">OpenSSH</span> <span class="kd">binaries</span> <span class="kd">are</span> <span class="kd">first</span> <span class="k">in</span> <span class="nv">%PATH%</span>
<span class="nb">where</span> <span class="kd">ssh</span>
<span class="kd">C</span>:\Windows\System32\OpenSSH\ssh.exe <span class="o">&lt;-</span> <span class="kd">this</span> <span class="kd">is</span> <span class="kd">first</span> <span class="k">in</span> <span class="nv">%PATH%</span> <span class="kd">and</span> <span class="kd">correct</span>
<span class="kd">C</span>:\Program <span class="kd">Files</span>\Git\usr\bin\ssh.exe 

<span class="nb">where</span> <span class="kd">ssh</span><span class="na">-add
</span><span class="kd">C</span>:\Windows\System32\OpenSSH\ssh<span class="na">-add</span>.exe <span class="o">&lt;-</span> <span class="kd">same</span>
<span class="kd">C</span>:\Program <span class="kd">Files</span>\Git\usr\bin\ssh<span class="na">-add</span>.exe

<span class="kd">ssh</span><span class="na">-add -L  </span># <span class="kd">succeeds</span>
<span class="kd">ssh</span><span class="na">-ed</span><span class="m">25519</span> ... <span class="kd">etc</span>

<span class="kd">git</span> <span class="kd">pull</span>  # <span class="kd">succeeds</span> <span class="kd">as</span> <span class="kd">is</span> <span class="kd">using</span> <span class="kd">the</span> <span class="kd">expected</span> <span class="kd">ssh</span> <span class="kd">binaries</span>
</code></pre></div></div>

<p>However, let’s do the same thing using git bash or similar (e.g. I have a Windows Terminal config that launches <code class="language-plaintext highlighter-rouge">C:\Program Files\Git\bin\bash.exe</code>).</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># git bash -- note: defaults to bundled git-provided ssh/ssh-add!</span>
<span class="nv">$ </span>which ssh
/usr/bin/ssh  <span class="c"># really C:\Program Files\Git\usr\bin</span>

<span class="nv">$ </span>which ssh-add
/usr/bin/ssh-add  <span class="c"># really C:\Program Files\Git\usr\bin</span>

<span class="nv">$ </span>ssh-add <span class="nt">-L</span>  <span class="c"># fails because it's using the wrong ssh binary</span>
Could not open a connection to your authentication agent.

<span class="nv">$ </span>git pull  <span class="c"># fails because using the wrong ssh binaries</span>
</code></pre></div></div>

<p>Running the <code class="language-plaintext highlighter-rouge">git config</code> line above (or manually editing the <code class="language-plaintext highlighter-rouge">~/.gitconfig</code> setting) fixes the problem and tells git where to find the correct ssh binaries which play nice with BitWarden.</p>

<p>After making the config fix, I could run <code class="language-plaintext highlighter-rouge">ssh-add -L</code> and also <code class="language-plaintext highlighter-rouge">git pull/fetch/push</code> as expected.</p>]]></content><author><name>Mark Simpson</name></author><category term="bitwarden" /><category term="ssh" /><category term="security" /><summary type="html"><![CDATA[Lots of hacks of late, some commonality I’ve been reading a fair number of post-mortems of late due to the number of npm supply chain hacks. The hackers often run post-install scripts to harvest credentials from the victim’s machine. That is: once you’ve been pwned, the hacker will iterate through a number of directories that commonly store sensitive credentials (e.g. ~/.aws, ~/.ssh, etc.)]]></summary></entry><entry><title type="html">Airplay on Windows with TuneBlade</title><link href="https://marksimpson82.github.io/blog/2025/12/21/tune-blade-windows-air-play.html" rel="alternate" type="text/html" title="Airplay on Windows with TuneBlade" /><published>2025-12-21T00:00:00+00:00</published><updated>2025-12-21T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/12/21/tune-blade-windows-air-play</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/12/21/tune-blade-windows-air-play.html"><![CDATA[<p>I’ve got a stereo with a <a href="https://uk.yamaha.com/en/audio/home-audio/products/wireless-streaming-amplifiers/wxc-50/">Yamaha WXC-50</a> streaming setup. It works great when you’re using a Mac, as you can use <a href="https://en.wikipedia.org/wiki/AirPlay">AirPlay</a> to stream audio directly over Wifi. This setup is seamless – you choose your Mac’s audio output as “LivingRoom” (or whatever you called your streaming receiver) and boom, you can stream your system audio to your stereo. No apps, no drama. Computer -&gt; WXC-50 -&gt; Stereo. Done!</p>

<p>When you’re on Windows, the suck factor is much higher because Windows doesn’t natively support AirPlay. Le sigh. I was reduced to a bunch of different (and often lesser) options:</p>

<h2 id="option-use-the-wxc-50s-built-in-web-app">Option: Use the WXC-50’s built-in web app</h2>
<p>Yeah, you can use this to play mp3s and whatnot, but it’s borderline unusable.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/blog/assets/images/2025/12/yamaha_wxc50_oh_dear.jpg" alt="The WXC-50 web interface is borderline unusable" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><em>A decidedly odd interface</em></td>
    </tr>
  </tbody>
</table>

<h2 id="option-use-yamaha-musiccast-app">Option: Use Yamaha MusicCast app</h2>
<p>The <a href="https://uk.yamaha.com/en/audio/home-audio/explore/musiccast/">Yamaha Music Cast</a> Android app is functional but clunky. It’s only a feasible option if you’re hosting your music in a location that is accessible via the network. In my case, I now have some of my music on my Synology NAS with discovery/indexing turned on. This means I can play music via the MusicCast Android app, but:</p>

<ol>
  <li>It’s clunky</li>
  <li>It doesn’t let me stream my Windows PC audio to my receiver, as that’s not what it was designed to do</li>
</ol>

<p>If I’m sitting at my PC, I don’t want to use my phone to navigate directories and pick songs. I want to play my music using foorbar2000 or whatever and hear my PC audio output.</p>

<h2 id="option-use-integrations-in-spotify-or-other-apps">Option: Use integrations in Spotify or other apps</h2>
<p>Like MusicCast, it works for certain use-cases but it doesn’t really suit my needs. I can’t play my own MP3s. I can’t stream my PC audio to my stereo.</p>

<h2 id="option-run-a-cable-from-the-pc-sound-card-to-the-stereo">Option: Run a cable from the PC sound card to the stereo</h2>
<p>No. I’ve got enough cables around the place, thanks.</p>

<h2 id="solution-tuneblade">Solution: <a href="https://www.tuneblade.com/">TuneBlade</a></h2>
<p>I honestly feel like a bit of a dope, as <a href="https://www.tuneblade.com/">TuneBlade</a> has been around for a while and is an AirPlay-compatible implementation for Windows that can stream to receivers. A license is a mere £8 – great value!</p>

<p>Steps:</p>
<ol>
  <li>Install TuneBlade</li>
  <li>Select your AirPlay-compatible device</li>
  <li>Reduce the audio delay down to sub-second (default is a 2s delay) to reduce lag</li>
  <li>Play some music</li>
</ol>

<p>That’s all there is to it! I can now stream my Windows PC audio to my Yamaha WXC-50 receiver. It’s not <em>quite</em> as slick as using a Mac, but it’s close.</p>]]></content><author><name>Mark Simpson</name></author><category term="windows" /><category term="airplay" /><category term="yamaha" /><category term="wxc50" /><summary type="html"><![CDATA[I’ve got a stereo with a Yamaha WXC-50 streaming setup. It works great when you’re using a Mac, as you can use AirPlay to stream audio directly over Wifi. This setup is seamless – you choose your Mac’s audio output as “LivingRoom” (or whatever you called your streaming receiver) and boom, you can stream your system audio to your stereo. No apps, no drama. Computer -&gt; WXC-50 -&gt; Stereo. Done!]]></summary></entry><entry><title type="html">Fixing AMD 7800X3D YouTube Crashes</title><link href="https://marksimpson82.github.io/blog/2025/11/22/amd-7800x3d-idle-crash.html" rel="alternate" type="text/html" title="Fixing AMD 7800X3D YouTube Crashes" /><published>2025-11-22T00:00:00+00:00</published><updated>2025-11-22T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/11/22/amd-7800x3d-idle-crash</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/11/22/amd-7800x3d-idle-crash.html"><![CDATA[<p><strong>Note</strong>: I used <a href="https://gemini.google.com/app">Gemini</a> when researching and solving the problem. However, I did <strong>not</strong> use AI to write this blog post. My voice is my own!</p>

<h2 id="7800x3d-and-youtube-instability">7800X3D and YouTube Instability</h2>
<p>This is a quick post about troubleshooting <a href="https://www.amd.com/en/products/processors/desktops/ryzen/7000-series/amd-ryzen-7-7800x3d.html">AMD 7800X3D CPU</a> crashes that occurred when watching YouTube, and YouTube only!</p>

<p>tl;dr: Try increasing the <code class="language-plaintext highlighter-rouge">CPU VDDCR_SOC Voltage</code> in the BIOS. I bumped it from default to 1.1v.</p>

<h2 id="new-pc">New PC</h2>
<p>Some of my earliest PC builds used AMD CPUs (opterons, bartons, whateverons), but my last couple of PCs have used Intel &amp; NVIDIA components.</p>

<p>My last PC was an Intel E8400 paired with an NVIDIA 1060 GTX (a workhorse!) which I stuck with for a long time due to the insanity of GPU pricing. It was rock-solid. I have had a few tempramental NVIDIA cards over the years, but nothing too bad.</p>

<p>I recently put together a new gaming PC with the following specs, and switched back to AMD:</p>

<table>
  <thead>
    <tr>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CPU</td>
      <td>AMD Ryzen 7 7800X3D</td>
    </tr>
    <tr>
      <td>CPU Cooler</td>
      <td>Thermalright Phantom Spirit 120 SE</td>
    </tr>
    <tr>
      <td>Motherboard</td>
      <td>Gigabyte B650 PLUS</td>
    </tr>
    <tr>
      <td>PSU</td>
      <td>Corsair 850W ATX - RM850x</td>
    </tr>
    <tr>
      <td>Memory</td>
      <td>Silicon Power XPOWER Zenith Gaming 32 GB DDR5-6000 CL30</td>
    </tr>
    <tr>
      <td>HDD</td>
      <td>Silicon Power UD90 2 TB M.2-2280 PCIe 4.0 X4 NVME</td>
    </tr>
    <tr>
      <td>GPU</td>
      <td>Sapphire PULSE Radeon RX 9060 XT 16 GB Video Card</td>
    </tr>
  </tbody>
</table>

<p>This is notable because it’s the first time I’ve run an AMD CPU since around the year 2010.</p>

<p><strong>Aside</strong>: I got 50% off the PSU by buying an official Corsair refurb product and probably 30% off the CPU by buying OEM from eBay. There are bargains to be had, you just need to test them carefully. I tested the PSU in my old PC build to minimise the risk of frying my new components.</p>

<h2 id="new-pc-new-problems">New PC, New Problems</h2>
<p>Here’s where things get irritating! Gaming? Rock-solid. Prime 95? Rock-solid. Everything else? Rock-solid. Watching YouTube videos? Not so much.</p>

<p>When watching YouTube videos, the PC reset itself multiple times per week. Sometimes I’d go a day or two between resets, but not much longer. The screen would switch off momentarily, then it’d boot back into Windows. Grrr!</p>

<h2 id="gathering-information">Gathering Information</h2>
<p>Event Viewer showed a Kernel-Power event with Level: <strong>Critical</strong>.</p>
<blockquote>
  <p>The system has rebooted without cleanly shutting down first. This error could be caused if the system stopped responding, crashed, or lost power unexpectedly.</p>
</blockquote>

<p>The interesting thing is that when under consistent load, the PC behaved itself and never once crashed or reset.</p>

<p>I wondered if perhaps there may be some tell-tale signs in logging information, so I installed <a href="https://www.hwinfo.com/">HWiNFO for Windows</a> which is a wonderful and lightweight hardware monitoring tool that can log an exhaustive list of metrics to a file.</p>

<p>I then captured a couple of crashes in the wild and graphed a bunch of metrics (temperatures, voltages, load values etc.) in the moments leading up to the crash. The problem is that it showed nothing of note. There was no smoking gun. Each crash seemed to show a spike in CPU usage a few seconds before the crash, then a reduction in load.</p>

<p>I originally did this by hand. I then fed the CSV file into <a href="https://gemini.google.com/app">Gemini 3</a> (I had a free month to mess with it) and asked it perform an analysis. I did not mention my own findings. It agreed with my analysis – going by the evidence, it was not a temperature or voltage issue.</p>

<h2 id="ok-so--what-now">OK, so … what now?</h2>
<p>I <del>googled</del> <a href="https://kagi.com/">Kagi</a> around and also asked Gemini for suggestions, and it came up with some plausible ideas, but nothing worked.</p>

<p>I’d already tried the following:</p>
<ul>
  <li>❌ Updating the Motherboard BIOS (did this after building the system)</li>
  <li>❌ Ensuring Precision Boost Overdrive (PBO) was disabled (it was)</li>
</ul>

<p>Gemini also suggested the following, but it didn’t help:</p>
<ul>
  <li>❌ Change “Power Supply Idle Control” to “Typical Current Idle”</li>
</ul>

<p>I searched around a bit more and found some new avenues, then returned to Gemini.</p>

<h2 id="eureka">Eureka</h2>
<p>The turning point was when I told Gemini that YouTube was always in the mix.</p>

<blockquote>
  <p>Me: The crash I described only happens when watching YouTube videos. Does that change your thinking on anything?</p>
</blockquote>

<blockquote>
  <p>Gemini: This detail is extremely significant. The fact that it happens specifically during YouTube playback (a light, fluctuating workload) strongly reinforces the “Low-Load Instability” diagnosis, but it also points to a very specific culprit: Hardware Acceleration and the SoC.</p>
</blockquote>

<p>Gemini then suggested making a bunch of software changes, such as:</p>
<ul>
  <li>Disabling hardware acceleration in Firefox</li>
  <li>Disabling Multi-Plane Overview (MPO)</li>
</ul>

<p>I thought these were pretty wide of the mark, as A) I have a GPU and intend to use it B) disabling MPO can affect FreeSync and other important functionality.</p>

<p>✅ Instead, I went with increasing the SoC voltage a notch, from 1.0v -&gt; 1.1v.</p>

<p>The results were pretty much instant (also: the “View Reliability History” tool is super useful and I had no idea it was a thing):</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><img src="/blog/assets/images/2025/12/system_reliability.jpg" alt="System Reliability Dialog showing the crashing stopping" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><em>No more crashes after the 15th</em></td>
    </tr>
  </tbody>
</table>

<h2 id="other-thoughts-re-amdintelnvidia">Other Thoughts re: AMD/Intel/NVIDIA</h2>
<p>I was not particularly impressed with my return to AMD CPU land! It was a pain in the arse, and a non-technical user would <strong>not</strong> figure this out – it would be an RMA.</p>

<p>Furthermore, AMD’s GPU software (Adrenaline) feels unimpressive and bloated. NVIDIA’s control panel has been a laggy POS for a decade+ at this point, but at least I don’t have to install the kitchen sink just to enable anti-lag mode and configure G-Sync.</p>

<p>No AMD, I don’t want your AI me-too chat bot in my GPU driver suite, thanks.</p>]]></content><author><name>Mark Simpson</name></author><category term="amd" /><category term="7800X3D" /><category term="crash" /><category term="youtube" /><summary type="html"><![CDATA[Note: I used Gemini when researching and solving the problem. However, I did not use AI to write this blog post. My voice is my own!]]></summary></entry><entry><title type="html">Windows 10 support ending? Linux Mint for my parents</title><link href="https://marksimpson82.github.io/blog/2025/10/27/linux-mint.html" rel="alternate" type="text/html" title="Windows 10 support ending? Linux Mint for my parents" /><published>2025-10-27T00:00:00+00:00</published><updated>2025-10-27T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/10/27/linux-mint</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/10/27/linux-mint.html"><![CDATA[<p>As a long-in-the-tooth software engineer, I’m getting to the end of my tether with Windows. I’ve lived through (and used) practically every version of Windows from 3.1 through to Windows 11, and the direction of travel is negative. Into the sewer, even.</p>

<p>I’ve used numerous versions as concerned citizen, student, adult and alleged professional:</p>
<ul>
  <li>Windows 3.1</li>
  <li>Windows 95</li>
  <li>Windows 98</li>
  <li>Windows 2k</li>
  <li>Windows ME (lol)</li>
  <li>Windows XP</li>
  <li>Windows Vista</li>
  <li>Windows 7 (I didn’t even bother with 8)</li>
  <li>Windows 10</li>
  <li>Windows 11</li>
</ul>

<p>There were a few high points in there. Windows 7 is up there, as is 98 and 2k (which I used because the mouse acceleration promised to make me a God tier FPS player – it did not). Hell, even the last years of Vista seemed vaguely pleasant. After the relative simplicity of Windows 7 it’s been downhill all the way, though.</p>

<h2 id="the-slow-death-of-windows-10">The Slow Death of Windows 10</h2>
<p>My memory of the first revision of Windows 10 is unreliable. What I can say for sure is that each new revision of Windows 10 came crammed full of bloat, advertising, spyware and even bullshit like pre-installed LinkedIn apps. I’ve got a PowerShell script (plus some manual instructions) to run after installing Windows, and each time I have to install Windows, the list needs updated.</p>

<p>Here’s a summary:</p>

<p>How do I…</p>
<ul>
  <li>Avoid creating a Microsoft account when installing the OS?</li>
  <li>Remove adverts from my lock screen?</li>
  <li>Remove celebrity news from my taskbar?</li>
  <li>Delete all of the bundled bullshit apps and bloatware?</li>
  <li>Uninstall all the XBox shit?</li>
  <li>Disable Cortana?</li>
  <li>Remove Bing search integration from the start menu?</li>
  <li>Lock down all of the privacy settings?</li>
</ul>

<p>This is hostile to the nth degree and completely direspects the user.</p>

<h2 id="windows-11---even-worse">Windows 11 - Even Worse</h2>
<p>I ‘upgraded’ to Windows 11 a few years back and it was just as bad, if not worse. There were a few more usability issues around context menus and UI consistency (how many flavours of menus are there now? When doing any kind of involved configuration, you have to drop back to Windows 95 era menus anyway!)</p>

<p>I binned it and went back to Windows 10.</p>

<h2 id="why-stay-on-windows-and-can-we-use-linux">Why Stay on Windows, and can we use Linux?</h2>
<p>I use MacOS at work and the only thing keeping me on Windows on my desktop is gaming. However, Linux is becoming more and more of a realistic player when it comes to gaming. I play FPS games that often have kernel anti-cheat (which don’t tend to work with non-Windows OSes), so there’s still a bit of a gap.</p>

<p>However, my parents have been running Windows since the 7 days, and guess what, they just tend to use their PC for web browsing and not much else. Could I chuck Linux on it and save them the Windows 11 experience? Also, many users will be running old hardware with no <a href="https://learn.microsoft.com/en-us/windows/security/hardware-security/tpm/trusted-platform-module-overview">Trusted Platform Module (TPM)</a>.</p>

<p>If you don’t have a motherboard with TPM, you cannot install Windows 11 (if you’re unsure, go check your BIOS – my 2017 motherboard had one, but it was disabled by default). Anyway, if you just want to browse the web, old hardware shouldn’t be thrown away to satisfy the spyware gods! If the hardware is capable, let’s use Linux!</p>

<h2 id="fedora-in-the-year-2018">Fedora in the Year 2018</h2>
<p>I’ve had a few aborted attempts at the Linux Desktop. The last one was ~2018 using Fedora for AI work stuff, and it was still a pain in the arse. My install ended with a bricked machine.</p>

<p>I installed the OS and spent ages dicking about getting the NVIDIA GPU drivers installed correctly. I then customised the OS and programs to my liking. A few hours later, I didn’t pin my NVIDIA driver package and bricked the machine doing <code class="language-plaintext highlighter-rouge">dnf upgrade</code> and wasted the best part of a day. Oops. Anyway, my point is it was rough around the edges.</p>

<h2 id="linux-mint-in-the-year-2025">Linux Mint in the Year 2025</h2>
<p>This time around, I plumped for <a href="https://linuxmint.com/">Linux Mint</a>. I followed the instructions, created a bootable USB pen drive and got stuck in. 15 minutes later I had a working Linux install with fully updated NVIDIA drivers, rolling backups and all of the basics working. Firefox was ready to go with uBlock Origin and audio worked perfectly. I didn’t notice any jank, advertising or spyware being slyly included, either (fancy that).</p>

<p>All in all, a very smooth experience. If you have friends or relatives that are in the same situation, it’s well worth a go. I can’t guarantee that your games will work with steam or <a href="https://www.winehq.org/">Wine</a>, but if you’re just browsing it’ll do the trick.</p>

<p>Might be worth trying a dual boot soon on my home desktop, too.</p>]]></content><author><name>Mark Simpson</name></author><category term="linux" /><category term="windows" /><category term="tpm" /><summary type="html"><![CDATA[As a long-in-the-tooth software engineer, I’m getting to the end of my tether with Windows. I’ve lived through (and used) practically every version of Windows from 3.1 through to Windows 11, and the direction of travel is negative. Into the sewer, even.]]></summary></entry><entry><title type="html">Making my Synology DS224+ NAS hibernate</title><link href="https://marksimpson82.github.io/blog/2025/10/27/synology-nas-hibernate.html" rel="alternate" type="text/html" title="Making my Synology DS224+ NAS hibernate" /><published>2025-10-27T00:00:00+00:00</published><updated>2025-10-27T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/10/27/synology-nas-hibernate</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/10/27/synology-nas-hibernate.html"><![CDATA[<h2 id="my-old-setup-intel-nuc-jellyfin-and-a-roku-stick">My old setup: Intel NUC, Jellyfin and a Roku stick</h2>
<p>I’ve run an Intel NUC with the <a href="https://en.wikipedia.org/wiki/OpenMediaVault">Open Media Vault (OMV)</a> distro and an external USB HDD since 2022. For my media server software, I started out with Plex and used it for a few years, but ultimately tired of the constant drip of stealthy on-by-default social features, live TV and so on. I swapped from Plex to Jellyfin and have been very happy with my choice. Jellyfin feels like a much more pleasant experience where my preferences are respected.</p>

<p>The final piece in the puzzle was a Roku 4k stick which meant I could disconnected my smart TV from the Internet. The Roku stick has superior Wi-Fi connectivity which meant I could ditch another Ethernet cable. While I do have concerns about Roku’s business ethics and approach to data collection, it’s better than relying on my archaic LG smart TV with its laggy interface and frequent crashes.</p>

<p>The NUC hardware was capable for transcoding, but I wasn’t having much fun on a few fronts:</p>
<ol>
  <li>Maintaining the OMV install (a bit of a, “don’t touch it” situation due to the configuration via GUIs)</li>
  <li>The NUC’s limited connectivity options meant I was in a bit of a storage dead-end.</li>
</ol>

<p>In short, I had a media PC with no Network Addressable Storage. My options were:</p>
<ol>
  <li>Buy a Direct Attached Storage (DAS) solution and run the NUC 24/7, turning it into a combination of poor man’s NAS and a media server</li>
  <li>Buy a Network Addressable Storage (NAS), but retain the NUC as a dedicated media server</li>
  <li>Buy a dedicated NAS and run the media server software on it</li>
</ol>

<p>I chose option 3, meaning I could retire my NUC.</p>

<h2 id="moving-to-the-ds224-nas">Moving to the DS224+ NAS</h2>
<p>I purchased a Synology DS224+ NAS and chucked a second-hand <code class="language-plaintext highlighter-rouge">WD HUH721212ALE600</code> 12TB drive in it on account of <a href="https://www.backblaze.com/blog/backblaze-drive-stats-for-q2-2025/">its excellent longevity</a> record (I also added a 12TB drive into my desktop PC for backup).</p>

<p>The DS224+ hardware is expensive for what it is, but it’s sufficient to run Jellyfin and direct-play 1080p x265/HEVC media.</p>

<h3 id="hardware-and-performance">Hardware and performance</h3>
<p>The hardware does struggle with transcoding at higher resolutions, but I avoid this by:</p>
<ol>
  <li>Favouring 1080p HEVC media</li>
  <li>Using bog-standard <a href="https://en.wikipedia.org/wiki/SubRip">SubRip</a> subtitles</li>
</ol>

<p>If you want to play 4k media, use PGS subtitles or transcode from other formats, I would recommend beefier hardware and/or a dedicated media server. E.g., when I tried to play a 4k AV1 file, it resulted in a locked system and then an error message. Oops!</p>

<h3 id="adding-cheap-unofficial-ram">Adding cheap, unofficial RAM</h3>
<p>The DS224+ comes with a meagre 2GB of RAM. I added another 4GB via <a href="https://www.mrmemory.co.uk/memory-ram-upgrades/synology/nas/ds224_">Mr. Memory</a> for £13 rather than the obscene official Synology RAM prices (£92 at the time of writing!)</p>

<p><strong>Note</strong>: While the official documents claim that 4GB is the maximum supported DIMM size, various posts on reddit claim that larger DIMMS will work.</p>

<h3 id="the-synology-os-diskstation-manager-dsm">The Synology OS: DiskStation Manager (DSM)</h3>
<p>Synology devices use <a href="https://en.wikipedia.org/wiki/Synology#DiskStation_Manager">DSM</a>: a Linux-derived OS that’s chiefly configured via web browser (depending on your preferences, this is either a pro or a con). While you can ssh into your NAS, don’t expect to find everything you expect from a Linux distro. E.g. when I was debugging the lack of hibernation, many standard unix tools were missing.</p>

<h3 id="dsms-file-browsing-and-shares-are-a-bit-weird">DSM’s file browsing and shares are a bit weird</h3>
<p>DSM has a package called <code class="language-plaintext highlighter-rouge">File Station</code> where you can browse your files. However, when you’ve got multiple drives and volumes, the association between volumes and folders/files is hidden. This information is available via right-clicking a file/folder and choosing “properties”, but it’s tucked away out of view.</p>

<p>To create a shared folder and choose the containing volume, you need to browse to <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">Shared Folder</code>.</p>

<p>E.g. I have a few shared folders set up as follows:</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Volume</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Media</td>
      <td>Volume 1 (12TB IDE)</td>
      <td>Media files (TV Shows, Films, Music)</td>
    </tr>
    <tr>
      <td>Docker</td>
      <td>Volume 2 (400GB SSD)</td>
      <td>Docker config, image files, jellyfin config etc</td>
    </tr>
  </tbody>
</table>

<h3 id="adding-an-ssd">Adding an SSD</h3>
<p>The DS224+ has two drive bays. I had an old 400GB SSD laying around, so I installed it as a second drive. My thinking was, “let’s move the OS and packages onto the SSD to reduce the disk chuntering”, but DSM doesn’t work like that.</p>

<p>Certain parts of the OS and its base packages are installed on all volumes. This is done for good reason: it means you can swap disks in and out, and you won’t brick the OS.</p>

<p>You can see the installation location of packages via browsing to <code class="language-plaintext highlighter-rouge">Package Center</code> &gt; <code class="language-plaintext highlighter-rouge">Installed</code> and selecting a package. Base packages will be listed as, “Installed volume: System partition”.</p>

<p>However, you <em>can</em> make your SSD the default installation location for custom packages to reduce the usage of your fatter, noisier disks. This is under <code class="language-plaintext highlighter-rouge">Package Center</code> &gt; <code class="language-plaintext highlighter-rouge">Settings</code> &gt; <code class="language-plaintext highlighter-rouge">Default Volume</code>.</p>

<p>I would strongly advise getting your drive/volume choices sorted out before you install and configure packages, as while tutorials exist for migrating packages between volumes, it’s not fool-proof or comprehensive. DSM uses symlinks for various pieces of configuration and it’s easy to break.</p>

<p>If you do find yourself needing to move packages between volumes, I would recommend doing the following:</p>

<ol>
  <li>Back up the configuration data</li>
  <li>Manually uninstall the package from the old volume</li>
  <li>Re-install the package on the new volume</li>
  <li>Restore the configuration data to the new volume</li>
</ol>

<p>Like I said, it’s much easier to get your volumes and their purpose configured up-front and save yourself the hassle.</p>

<h3 id="installing-jellyfin">Installing Jellyfin</h3>
<p>Installing Jellyfin was straightforward:</p>

<ol>
  <li>Using <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">Shared Folder</code>, create shares for docker and your media on the appropriate volumes (already covered above)</li>
  <li>Install and Open <a href="https://www.synology.com/en-br/dsm/feature/docker">Container Manager</a></li>
  <li>Click the <code class="language-plaintext highlighter-rouge">Projects</code> tab</li>
  <li>Add a project called “Jellyfin”, and the path should be using the fastest/quietest drive volume (for me: <code class="language-plaintext highlighter-rouge">/volume2/docker/jellyfin</code>)</li>
  <li>Add a docker-compose.yaml (while you can naively just start a container based on the <code class="language-plaintext highlighter-rouge">jellyfin/jellyfin</code> image, there’s a load of implicit config involved that won’t be reproducible – better to just use a docker-compose file from the start).</li>
</ol>

<p>A sample docker-compose file:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">jellyfin</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">jellyfin/jellyfin</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">jellyfin</span>
    <span class="na">healthcheck</span><span class="pi">:</span>
      <span class="c1"># disable health check to avoid excess disk activity</span>
      <span class="na">disable</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">ports</span><span class="pi">:</span>
      <span class="c1"># standard ports</span>
      <span class="pi">-</span> <span class="s">8096:8096/tcp</span>
      <span class="pi">-</span> <span class="s">7359:7359/udp</span>
    <span class="na">volumes</span><span class="pi">:</span>
      <span class="c1"># config &amp; cache uses a fast/quiet SSD if possible</span>
      <span class="pi">-</span> <span class="s">/volume2/docker/jellyfin/config:/config:rw</span>
      <span class="pi">-</span> <span class="s">/volume2/docker/jellyfin/cache:/cache:rw</span>
      <span class="c1"># media is stored on my fat 12TB spinning rusk disk</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">bind</span>
        <span class="na">source</span><span class="pi">:</span> <span class="s">/volume1/media</span>
        <span class="na">target</span><span class="pi">:</span> <span class="s">/media</span>
        <span class="na">read_only</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s1">'</span><span class="s">unless-stopped'</span>    
</code></pre></div></div>

<h3 id="fixing-hibernation">Fixing Hibernation</h3>
<p>There was one fly in the ointment: my DS224+ refused to hibernate, and this seems to be a common problem. My intial install used the 12TB IDE disk for everything, and it’s <em>loud</em>. Not good.</p>

<p>Also, with all drives running, we’re probably talking an extra 10-15W of power usage 24/7. At 25p/KWh that adds up to £2-3 per month, plus the extra wear and tear on the drives.</p>

<p>I started working through the <a href="https://kb.synology.com/en-uk/DSM/tutorial/What_stops_my_Synology_NAS_from_entering_System_Hibernation">official documentation / checklist of items that may prevent hibernation</a> but it’s a bit of a kitchen sink affair.</p>

<p>Here’s what worked for me (though your mileage may vary):</p>
<ol>
  <li>Move packages (ContainerManager) and config (Docker) to my SSD</li>
  <li>Disable Jellyfin’s docker healthcheck via <code class="language-plaintext highlighter-rouge">healthcheck: disable</code></li>
  <li>Stop all non-essential packages</li>
  <li>Disable the SSH service via <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">Terminal &amp; SNMP</code> &gt; <code class="language-plaintext highlighter-rouge">Terminal</code> &gt; uncheck <code class="language-plaintext highlighter-rouge">Enable SSH Service</code></li>
  <li>Disable the bonjour service via <code class="language-plaintext highlighter-rouge">Control Panel</code> &gt; <code class="language-plaintext highlighter-rouge">File Services</code> &gt; <code class="language-plaintext highlighter-rouge">Bonjour</code> &gt; uncheck <code class="language-plaintext highlighter-rouge">Enable Bonjour</code></li>
  <li>Unmap any network drives (I had my NAS mapped via my desktop PC)</li>
  <li>Configure tasks to run less frequently via this <a href="https://www.reddit.com/r/synology/comments/10cpbqd/making_disk_hibernation_work_on_synology_dsm_7/">reddit thread</a> (I moved some of the daily tasks to run weekly and stopped there).</li>
</ol>

<p>After running through these steps, my NAS now hibernates after idling for a while. Success!</p>

<p><strong>Note</strong>: It <em>does</em> take ~30 seconds to wake up and become interactive. If that’s too sluggish for you, then maybe just leave it running 24/7.</p>]]></content><author><name>Mark Simpson</name></author><category term="dsm" /><category term="synology" /><category term="nas" /><summary type="html"><![CDATA[My old setup: Intel NUC, Jellyfin and a Roku stick I’ve run an Intel NUC with the Open Media Vault (OMV) distro and an external USB HDD since 2022. For my media server software, I started out with Plex and used it for a few years, but ultimately tired of the constant drip of stealthy on-by-default social features, live TV and so on. I swapped from Plex to Jellyfin and have been very happy with my choice. Jellyfin feels like a much more pleasant experience where my preferences are respected.]]></summary></entry><entry><title type="html">Private Blogging (a.k.a. why this blog is a barren wasteland)</title><link href="https://marksimpson82.github.io/blog/2025/02/26/private-blogging.html" rel="alternate" type="text/html" title="Private Blogging (a.k.a. why this blog is a barren wasteland)" /><published>2025-02-26T00:00:00+00:00</published><updated>2025-02-26T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2025/02/26/private-blogging</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2025/02/26/private-blogging.html"><![CDATA[<h2 id="q-how-do-you-take-notes-a-privately">Q: How do you take notes? A: Privately.</h2>
<p>As a follow-on from <a href="/blog/2022/10/03/obsidian.html">How do you take notes?</a>, I thought I’d write a (very) short update.</p>

<p>tl;dr: I’ve been using Obsidian for my personal note-taking since 2022, and I really like it.</p>
<ul>
  <li>I have a private git repository</li>
  <li>I write my notes in markdown (just like this blog)</li>
  <li>I don’t have to care about whether the subject I’m writing about is boring or suitable for sharing (like this!)</li>
  <li>I am free to mix plain text with code snippets, or reference (small) files, whatever
    <ul>
      <li>This is especially useful for things I’ve partly automated and use infrequently</li>
    </ul>
  </li>
  <li>I can come back to things I’d often forget, e.g. boiler part numbers / instructions</li>
</ul>

<h2 id="downsides">Downsides</h2>
<p>The downside is that it’s temporarily killed this blog.</p>

<p>It may look like I’m in a coma, but I am still furiously writing. The posts are not shared, though. When I have to tackle a subject, I tend to just write my own notes to clarify my own thoughts, e.g. in no particular order:</p>
<ul>
  <li>Git spells and internals</li>
  <li>Testing microservices</li>
  <li>The boundaries of various testing / faking approaches
    <ul>
      <li>In process</li>
      <li>Out of process</li>
      <li>Using tools like Wireshark</li>
    </ul>
  </li>
  <li>Learning golang</li>
  <li>Learning AI/LLM fundamentals (I won’t subject anyone to this, plenty of awesome resources out there!)</li>
</ul>

<h2 id="public-blogging">Public blogging</h2>
<p>I’ve had to fix my public blog a few times because the GitHub action that publishes broke a few times. It’s now back up.</p>

<p>I will try and cherry pick a few interesting topics and re-shape a few for my public blog.</p>

<p>Here’s a sample of what I’ve been writing about but not sharing.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># print total line count for markdown files; </span>
<span class="c"># show the largest by line count in desc. order</span>
<span class="nv">$ </span>find <span class="nb">.</span> <span class="nt">-name</span> <span class="s1">'*.md'</span> | xargs <span class="nb">wc</span> <span class="nt">-l</span> | <span class="nb">sort</span> <span class="nt">-nr</span>
 17446 total
   <span class="c"># I've got a few git presentations in me at this point</span>
   774 ./it_and_software/software_engineering/git_internals.md
   349 ./it_and_software/software_engineering/git_spells.md
      
   <span class="c"># Open Media Vault / Jellyfin install steps / config 📼</span>
   332 ./it_and_software/install_omv.md
   
   <span class="c"># Andrej Karpathy's videos are great primers / explainers</span>
   <span class="c"># on modern LLMs, recommended! 🤖</span>
   330 ./it_and_software/software_engineering/ai/ai_karpathy_001_llm_overview.md
   
   <span class="c"># Is it a data lake? No! It's a data lake ... house, of course 🤦‍♂️</span>
   319 ./data/data_lakehouse.md
   
   <span class="c"># Had to use 🥒. Do not like ❌. It has its place, but customers</span>
   <span class="c"># and non-engineers often want it, but then don't contribute</span>
   <span class="c"># so you have extra complexity to carry for little gain</span>
   301 ./it_and_software/testing/cucumber_gherkin.md

   <span class="c"># Data testing with great expectations 👩‍💻🧪. </span>
   <span class="c"># Works OK with batch, but if upstream data is bad... welp</span>
   284 ./data/testing/great_expectations/ge_approaches.md

   <span class="c"># Microservice 'component' testing. Check out:</span>
   <span class="c"># - Martin Fowler's stuff</span>
   <span class="c"># - Cindy Sridharan's 'step up rule', too.</span>
   <span class="c"># https://copyconstruct.medium.com/testing-microservices-the-sane-way-9bb31d158c16</span>
   262 ./it_and_software/testing/microservice_testing.md 
   
   <span class="c"># Had to write one at 🔫👈, but then nobody reads it 😭</span>
   <span class="c"># Cleaned up a few templates for future projects</span>
   250 ./it_and_software/testing/qa_test_strategy.md
   201 ./it_and_software/testing/qa_test_plan.md
   
   <span class="c"># hx is an alternative to vi/vim/neovim. Works well.</span>
   <span class="c"># I wouldn't bother switching if you know vi/vim, though</span>
   235 ./it_and_software/helix_editor.md

   <span class="c"># go's out of the box testing is good,</span>
   <span class="c"># but the boilerplate can be irritating</span>
   234 ./it_and_software/testing/go_testing.md

   <span class="c"># won't it ever be the year of linux desktop? </span>
   <span class="c"># Give me 100% modern gaming on Linux and I'm gone 🏃‍♂️💨</span>
   233 ./it_and_software/install_windows10.md

   <span class="c"># won't it ever be the year of heat pumps in old UK flats, ffs? </span>
   <span class="c"># Heat Geeks for 🧠🎓 though! https://www.heatgeek.com/</span>
   223 ./home_and_diy/boiler_replacement.md
 
   <span class="c"># playwright is great for headless browser testing &amp; automation</span>
   183 ./it_and_software/testing/playwright.md

   <span class="c"># my .gitconfig and spells 👩‍💻</span>
   172 ./it_and_software/software_engineering/git_config.md

   <span class="c"># etc   </span>
</code></pre></div></div>

<p>… and so on.</p>]]></content><author><name>Mark Simpson</name></author><category term="blogging" /><category term="obsidian" /><category term="note-taking" /><summary type="html"><![CDATA[Q: How do you take notes? A: Privately. As a follow-on from How do you take notes?, I thought I’d write a (very) short update.]]></summary></entry><entry><title type="html">The Herman Miller Aeron (tall edition)</title><link href="https://marksimpson82.github.io/blog/2022/11/13/herman-miller-aeron.html" rel="alternate" type="text/html" title="The Herman Miller Aeron (tall edition)" /><published>2022-11-13T00:00:00+00:00</published><updated>2022-11-13T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2022/11/13/herman-miller-aeron</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2022/11/13/herman-miller-aeron.html"><![CDATA[<h2 id="tldr">tl;dr</h2>
<ul>
  <li>You may need to reduce the Aeron seat height or it’ll destroy your hamstrings</li>
  <li>If you reduce your seat height, make sure your desk can accomodate it</li>
  <li>If you buy a standing desk like the Fully Jarvis, don’t scrimp on accessories; future-proof it with the 3-stage frame!</li>
</ul>

<h2 id="expensive-chairs-and-some-frustration">Expensive chairs and some frustration</h2>
<p><strong>TODO</strong>: picture of Aeron</p>

<p>This post serves as a warning for Tall People who decide to spend an unreasonable amount of money on a chair. 
If you want to hear about some potential problems with Aerons for tall people, read on.</p>

<p>Before I start yammering on, here’s a good video on how to find/buy an Aeron for less.</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/x-aeDaHIEbY" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p>The one recommendation I can 100% make is not to buy an Aeron (or any chair) without trying it first. Sit in it for an 
hour or more if possible.</p>

<p>Even if you do this, it won’t necessarily be 100% indicative of whether the chair is right for you.</p>

<h2 id="aeron-sizing">Aeron sizing</h2>
<p>The Aeron comes in 3 progressively larger sizes: A, B and C. There’s also a size chart that offers some vague 
recommendations.</p>

<p><strong>TODO</strong>: picture of aeron sizing guide</p>

<p>I’m 6’6 (198cm) tall and wear 38” in-seam jeans. In short, I’m all legs. As a consequence, I replaced the gas cylinder 
in my previous chair with a longer one. This allowed me to sit in an ergonomic, comfortable position with my knees and 
hips in allignment and my thigh parallel to the ground. As a consequence of the raised seat height, I also increased my 
desk height with a desk risers.</p>

<p>I tried my friend’s size B and it was comfortable, if a little small. I was worried about the seat height &amp; gas cylinder 
length, but even the size B seemed perfectly fine. I did feel like the back rest was a little short, so I plumped for a 
size C.</p>

<p>Anyway, the point of this post is that the Aeron is <em>different</em>. Not bad or good different, just different.</p>

<h2 id="death-to-your-hamstrings">Death to your hamstrings</h2>
<p>When I took delivery of my size C Aeron, I changed its seat height to match my previous setup – I wanted my elbows at 
desk height. All seemed good.</p>

<p>After a week or use, my lower back (and back in general) was feeling excellent. However, my hamstrings were dying. They 
felt absolutely <em>dreadful</em>. For whatever reason, sitting at a like-for-like height seems to put a lot of weight on my 
hamstrings/haunches and less through my feet.</p>

<h2 id="the-fix-lower-the-seat-height">The fix: lower the seat height</h2>
<p>I did a bit of searching, and the main suggestion was to lower the seat. After following this advice, everything was 
fixed. It took a few days for my aches to fully disappear, but smooth sailing resumed. Problem solved!</p>

<p>For whatever reason, sitting lower in the Aeron feels a lot better and works fine – possibly because the seat is deeper
than my old one and/or because the Aeron is constructed from a pellicle mesh, and you sink down into it a little more. I
can’t really say for sure.</p>

<h2 id="death-to-your-desk">Death to your desk</h2>
<p>Unfortunately, this change has a knock-on effect: because I’m sitting lower in the chair, my elbows are now well below 
the desk!</p>

<p>For my work desk, this is no problem at all – I can simply remove one set of desk risers to get the perfect height.</p>

<p>On the other hand, my gaming PC sits on a Fully Jarvis standing desk and … well, I’m stuck. The lowest this desk goes 
is around 80cm (73cm + a 7cm thick custom worktop). When buying the Jarvis, you can pay +£40 to extend the range with 
the 3-stage frame (range: 62-127cm). That would’ve given me an effective range of 69-134cm.</p>

<p><strong>TODO</strong>: picture of jarvis</p>

<p>I didn’t bother. I knew the exact dimensions I needed to work with my usual chair height &amp; ergonomics, so why spend the 
extra? Well, I really wish I did now! If you’re buying a Jarvis (or any standing desk) I would strongly encourage you
to spend a little more for the improved long-term flexibility.</p>]]></content><author><name>Mark Simpson</name></author><category term="chairs" /><category term="herman_miller" /><category term="aeron" /><category term="tall" /><category term="fully_jarvis" /><category term="desk" /><category term="standing_desk" /><summary type="html"><![CDATA[tl;dr You may need to reduce the Aeron seat height or it’ll destroy your hamstrings If you reduce your seat height, make sure your desk can accomodate it If you buy a standing desk like the Fully Jarvis, don’t scrimp on accessories; future-proof it with the 3-stage frame!]]></summary></entry><entry><title type="html">Taking notes with Obsidian</title><link href="https://marksimpson82.github.io/blog/2022/10/03/obsidian.html" rel="alternate" type="text/html" title="Taking notes with Obsidian" /><published>2022-10-03T00:00:00+00:00</published><updated>2022-10-03T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2022/10/03/obsidian</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2022/10/03/obsidian.html"><![CDATA[<h2 id="how-do-you-take-notes">How do you take notes?</h2>

<p>I’ve been programming professionally since ~2008, and I’ve tried quite a few different approaches during my career.</p>

<h3 id="approach-pencil-and-paper">Approach: pencil and paper</h3>
<p>There’s something nice about low technology note-taking. I used paper for a good chunk of my time at eeGeo/WRLD3D.</p>

<p>Pros:</p>
<ul>
  <li>Using paper lets you (forces you!) to step away from the keyboard for a moment</li>
  <li>You can take your pad elsewhere and prod at your thoughts elsewhere</li>
  <li>There’s no real substitute for sketching diagrams quickly on paper</li>
  <li>You can easily draw attention to more important ideas via highlighters etc.</li>
</ul>

<p>Cons:</p>
<ul>
  <li>Not searchable without a filing system, and even then it won’t be as good as grep</li>
  <li>You need to file the notes somewhere if you want to keep them around</li>
  <li>They take up space</li>
  <li>You can’t easily copy paste via your computer</li>
  <li>You can’t use hyperlinks or other embedded, digital resources</li>
</ul>

<h3 id="approach-shared-wiki">Approach: shared wiki</h3>
<p>We were forced to use a wiki at Realtime Worlds to log our work notes for the day. The idea was that if we invented &amp; patented something novel, we’d have a trail of evidence.
Similarly, if somebody tried to patent-troll us, we’d also have evidence to refute the claim.</p>

<p>Pros:</p>
<ul>
  <li>It was actually kind of nice to have a formalised log of our work / thoughts</li>
  <li>It was <em>somewhat</em> searchable</li>
</ul>

<p>Cons:</p>
<ul>
  <li>It was cumbersome to edit</li>
  <li>The search was patchy</li>
</ul>

<p>On top of this, there were a few problems with <em>enforced &amp; public</em> note-taking (kind of conflated with the wiki model):</p>
<ul>
  <li>Having to produce one’s thoughts on demand was a burden</li>
  <li>You couldn’t write honestly about things</li>
</ul>

<h3 id="approach-haphazard-txt-files">Approach: haphazard .txt files</h3>
<p>For short-term tactical work, text files aren’t a bad choice. You can jot your thoughts down, add links to read and search through it later.
I tended to just shove them in a .txt file named after the bug number, or the name of the feature I was working on. 
Again, this is something I did a lot of at eeGeo/WRLD3D.</p>

<p>It wasn’t perfect, but it did have its uses.</p>

<p>Pros:</p>
<ul>
  <li>You can group notes by the topic easily enough</li>
  <li>It’s searchable</li>
</ul>

<p>Cons:</p>
<ul>
  <li>Doesn’t support rich media</li>
  <li>Without conventions &amp; some level of care, it becomes disorganised</li>
</ul>

<p>In my case, I tended to end up with a temp directory full of notes. I’d fish out the important-looking stuff and commit it somewhere (google doc, bug tracker, commit messages, comments, etc.) and the rest would rot.</p>

<p>After a while, I’d have to switch computers and lose the rest. Not great. There’s been quite a few times where I came up with a pretty smart, novel approach to solving an interesting problem. I can’t remember the details.</p>

<h3 id="approach-using-a-tool-or-a-hybrid-model">Approach: Using a tool (or a hybrid model)</h3>
<p>I’ve been using <a href="https://obsidian.md/">Obsidian</a> since early 2022, and I’m really, really liking it.</p>

<p>While I heartily recommend Obsidian, it’s not the only note-taking app in town. <a href="https://orgmode.org/">Org Mode</a> is another much talked about tool, for example.</p>

<p>Why do I enjoy Obsidian? Well, it allows me to document anything and everything and keep to a routine.</p>

<p>Pros:</p>
<ul>
  <li>It has sane defaults</li>
  <li>Support for tagging &amp; linking between notes</li>
  <li>Support for templated, daily notes</li>
  <li>Support for images and other media</li>
  <li>It has a plugin system</li>
  <li>The app itself (on Windows at least) is fairly lightweight
    <ul>
      <li>27MB of RAM used on my Windows machine</li>
    </ul>
  </li>
  <li>Multi-platform support (I use it on Windows &amp; MacOS)</li>
  <li>Mobile support</li>
  <li>Markdown format, so no crazy proprietary stuff
    <ul>
      <li>Which means you can read/write notes however you like</li>
      <li>If you stop using Obsidian, your notes are in plaintext</li>
    </ul>
  </li>
</ul>

<p>Cons:</p>
<ul>
  <li>Closed source</li>
  <li>Over the ~8 months or so I’ve been using it, they’ve changed the style/layout a lot</li>
  <li>Not everyone will like using a dedicated GUI app</li>
  <li>If you use it professionally you have to pay $50 p/a</li>
</ul>

<h4 id="routine">Routine</h4>
<p>Obsidian helped me to reinforce a routine. When I open Obsidian, it presents today’s daily note, which looks like this:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh"># Daily Notes</span>
tags: #daily

<span class="gu">## TODO</span>
<span class="p">-</span> [ ] Daily walk
<span class="p">-</span> [ ] Brush teeth at lunchtime

<span class="gu">## General</span>
</code></pre></div></div>
<p>The act of checking off items definitely helps.</p>

<p>My 2 items are currently:</p>
<ol>
  <li>Since COVID-19, lockdown and WFH started, I became a bit more … torpid. I <em>always</em> want to leave the house. Even if I’m doing other exercise, it’s important to keep moving via walking.</li>
  <li>When I worked in an office, I brushed my teeth after lunch. I lost that habit after the office was torpedoed. My dentist has been nagging me about it.</li>
</ol>

<p>I now face the reality of my laziness via my daily notes; an unchecked box looks very bad. It’s just a walk. It’s literally just a 90-second brushing. Those boxes tend to get checked now.</p>

<p>In addition to a daily template, Obsidian comes in really handy for noting things that I’d frequently forget.</p>

<p>For example:</p>
<h4 id="jobs-interviews--people">Jobs interviews &amp; people</h4>
<p>I had a job interview at the end of 2021 (which I failed), and another 4 or 5 at the start of 2022. A few of those were early in the process after a few phone interviews, so I dropped out because I’d received offers.</p>

<p>After that round of interviews concluded, pre-Obsidian me would’ve:</p>
<ul>
  <li>Had only a few emails and details to go on</li>
  <li>Thrown out his notes (either because I formatted my PC, or because a notebook got binned)</li>
  <li>Half-heartedly addressed the weaknesses I needed to look at</li>
  <li>Barely remembered much about it</li>
  <li>Probably not followed through</li>
</ul>

<p>Post-Obsidian me does this a lot better.</p>
<ul>
  <li>From each company, I know every person I spoke to, their job titles etc.
    <ul>
      <li>I also can remember the people who made a good impression on me and look them up in future!</li>
    </ul>
  </li>
  <li>I have an overview of the interview process
    <ul>
      <li>How many stages</li>
      <li>The questions they asked me</li>
      <li>The answers I gave</li>
      <li>My own thoughts on how I performed</li>
    </ul>
  </li>
  <li>Why I didn’t get the job or why I withdrew from the process</li>
  <li>Or if I did get an offer, exactly what it was</li>
</ul>

<p>It’s all tied together in a neat bundle and ready to be mined. Committing these thoughts to notes doesn’t take long, and I find writing about it provides a lot of opportunities for self-reflection.</p>

<p>I had been working at my previous company for 11 years, so it was important to me that I didn’t let job-hunting details wash over me.</p>

<h4 id="work-notes">Work notes</h4>
<p>I’ve been working for Infinity Works since March 2022, and in that time I’ve had to learn new things rapidly.</p>

<p>Note-taking has been useful for organising my thoughts, collecting links and writing my own take on the things I’ve learned.</p>

<p>For example, the first gig involved <a href="https://www.databricks.com/">Databricks</a>, <a href="https://greatexpectations.io/">Great Expectations</a>, <a href="https://azure.microsoft.com/en-gb/products/devops/">Azure DevOps</a> and many other topics I’d never touched before.</p>

<p>Writing was a really useful way to figure out how much I’d learned and how far I still had to go. It helped with prioritisation on what to learn, too. If I forgot something or lost my place, it was written down.</p>

<h4 id="semi-automation-notes">Semi-automation notes</h4>
<p>Have you ever installed &amp; configured software, but didn’t fully automate it? E.g. at work, you’d tend to have a machine image or a series of scripts to install everything just so. At home? Not so much. Too much effort for something you do once every couple of years.</p>

<p>Can you remember:</p>
<ul>
  <li>Everything you need to back up before you wipe your machine?</li>
  <li>The irritating Windows options you need to toggle?</li>
  <li>All the programs you’ve got installed</li>
  <li>The obscure configuration changes you made?</li>
</ul>

<p>It’s often said that the first step to automation is writing out a list. Notes are the sweet spot for infrequently performed tasks.</p>

<p>E.g. here’s an excerpt from my Windows install notes (automating this would be overkill because it’s complicated and rarely gets used):</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">1.</span> Go to "Add or Remove Programs" and uninstall everything possible
<span class="p">  1.</span> Can also use <span class="sb">`Remove-AppxPackage`</span> to do this (see below for Cortana)  
<span class="p">2.</span> Go to Apps &amp; Features -&gt; Optional Features
<span class="p">  1.</span> Uninstall unneeded programs
<span class="p">3.</span> Taskbar
<span class="p">  1.</span> Disable all taskbar integrations, like people/events/weather 
<span class="p">  2.</span> Display all taskbar icons
<span class="p">  3.</span> Don't combine taskbar entries
<span class="p">  4.</span> Display full taskbar entries on all monitors
<span class="p">4.</span> Start menu
<span class="p">  1.</span> Delete everything, inc. live tiles etc
<span class="p">  2.</span> Disable web search in the start menu (I think it was <span class="p">[</span><span class="nv">this</span><span class="p">](</span><span class="sx">https://www.bennetrichter.de/en/tutorials/windows-10-disable-web-search/</span><span class="p">)</span>)
<span class="p">5.</span> Delete Cortana and other Windows Apps nonsense
</code></pre></div></div>

<p>Then an embedded powershell script to remove software components that I cribbed from other sources:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-AppxPackage</span><span class="w"> </span><span class="nt">-allusers</span><span class="w"> </span><span class="nx">Microsoft.549981C3F5F10</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Remove-AppxPackage</span><span class="w">
</span><span class="nx">Get-AppxPackage</span><span class="w"> </span><span class="nt">-allusers</span><span class="w"> </span><span class="nx">Microsoft.WindowsMaps</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Remove-AppxPackage</span><span class="w">
</span><span class="nx">Get-AppxPackage</span><span class="w"> </span><span class="nt">-allusers</span><span class="w"> </span><span class="nx">Microsoft.People</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Remove-AppxPackage</span><span class="w">
</span><span class="c"># etc</span><span class="w">
</span></code></pre></div></div>

<p>Finally, I have a <a href="https://chocolatey.org/">chocolatey</a> script to install my staple programs along with a list of software that’s not available via chocolatey.</p>

<p>This sort of thing is great. I would never remember many of these tips, and I’m free to mix in scripts with instructions as and when it makes sense.</p>

<h4 id="random-things">Random things</h4>
<p>I’ve written quite a few things over the last year, and keeping the notes has been very useful.</p>

<p>For example:</p>
<ul>
  <li>Boiler problems / solutions, and cost/benefit tradeoffs of fixing vs. replacing
    <ul>
      <li>Along with niche sites that sell parts for my 2003 boiler!</li>
      <li>I saved £150, and I have a list of other parts in case it fails again</li>
    </ul>
  </li>
  <li>How to find a good value refurbished Aeron chair</li>
  <li>Laser eye surgery options</li>
  <li>Mortgage</li>
  <li><a href="https://rootmy.tv/">Rooting my TV</a></li>
  <li>Setting up <a href="https://www.openmediavault.org/">Open Media Vault</a> &amp; <a href="https://www.plex.tv/">Plex</a></li>
  <li>Fixing my 2010 Kindle when it refused to login (Amazon support were useless, had to figure it out myself)</li>
  <li>How to build a <a href="https://www.youtube.com/watch?v=l4uCRuO-Ayo">Corsi-Rosenthal Cube</a> (it was hard to find UK resources)</li>
  <li>De-bloating a Lenovo tablet</li>
  <li>Recording a few medical issues I’d been having, along with detailed timelines
    <ul>
      <li>I <em>really</em> wish I had done this in 2020 when COVID appeared! (I got long COVID, but now I can’t remember how long I was KO’ed by it)</li>
    </ul>
  </li>
</ul>

<p>The only downside to doing a lot of personal note-taking is that it saps the blogging energy.</p>

<h2 id="helix">Helix</h2>
<p>Oh, and I’ve also been using <a href="https://helix-editor.com/">Helix</a> to write a lot of my notes rather than Obsidian.</p>

<p>Why? Well, it’s very lightweight, accessible from the terminal (which is always open) and I felt like taking my mediocre vim skills a bit further.</p>

<p>I added the following alias to my <code class="language-plaintext highlighter-rouge">.bashrc</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">today</span><span class="o">=</span><span class="s2">"cd /c/work/notes &amp;&amp; ./today.sh"</span>
</code></pre></div></div>

<p>… which then calls this janky script (this is on Windows):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">set</span> <span class="nt">-euo</span> pipefail
  
<span class="nb">readonly </span><span class="nv">today</span><span class="o">=</span>./daily/<span class="si">$(</span><span class="nb">date</span> +%Y<span class="si">)</span>/<span class="si">$(</span><span class="nb">date</span> +%m<span class="si">)</span>/<span class="si">$(</span><span class="nb">date</span> +%F<span class="si">)</span>.md
  
<span class="k">if</span> <span class="o">[[</span> <span class="o">!</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$today</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$today</span><span class="s2"> doesn't yet exist; creating from template..."</span>
  <span class="nb">cp</span> <span class="s2">"./daily/daily_template.md"</span> <span class="s2">"</span><span class="nv">$today</span><span class="s2">"</span>
<span class="k">fi
          
</span>hx <span class="s2">"</span><span class="nv">$today</span><span class="s2">"</span>
</code></pre></div></div>

<p>I’m quite enjoying Helix, and it’s replaced vim for my utility editor (git commit messages, etc.).</p>

<h2 id="appendix-synchronisingbacking-up">Appendix: synchronising/backing up</h2>
<p>If you’ve got your notes on one PC, you still need to make them portable or, at the very least, back them up regularly.</p>

<p>There are various ways to do this, and many of them work with with whatever digitised method you choose.</p>

<p>Obsidian has sync as a paid feature, but there other free (or free-ish) ways of doing it:</p>
<ul>
  <li>Store your notes in a shared directory with Dropbox, Google Drive, OneDrive etc.</li>
  <li>Add them to version control</li>
</ul>

<p>I personally store my work-related notes in Google Drive directory and/or OneDrive depending on the environment.</p>

<p>For personal notes, I synch via a git repo. This is a little cumbersome, but I can live with it.</p>]]></content><author><name>Mark Simpson</name></author><category term="obsidian" /><category term="note-taking" /><category term="helix" /><summary type="html"><![CDATA[How do you take notes?]]></summary></entry><entry><title type="html">Parameterising sgen (aka the .NET Microsoft.XmlSerializer.Generator) via a .csproj PropertyGroup</title><link href="https://marksimpson82.github.io/blog/2021/11/04/dotnet5-sgen-parameters.html" rel="alternate" type="text/html" title="Parameterising sgen (aka the .NET Microsoft.XmlSerializer.Generator) via a .csproj PropertyGroup" /><published>2021-11-04T00:00:00+00:00</published><updated>2021-11-04T00:00:00+00:00</updated><id>https://marksimpson82.github.io/blog/2021/11/04/dotnet5-sgen-parameters</id><content type="html" xml:base="https://marksimpson82.github.io/blog/2021/11/04/dotnet5-sgen-parameters.html"><![CDATA[<h2 id="the-problem">The problem</h2>
<p>If you’ve landed on this post via searching the web, you probably already know what sgen.exe is, and what the 
<a href="https://docs.microsoft.com/en-us/dotnet/core/additional-tools/xml-serializer-generator"><code class="language-plaintext highlighter-rouge">Microsoft.XmlSerializer.Generator</code></a> 
nuget package does.</p>

<p>You probably used to <a href="https://docs.microsoft.com/en-us/dotnet/standard/serialization/xml-serializer-generator-tool-sgen-exe">invoke sgen</a> 
as part of a build script or a post-build .csproj step and parametrise it as you saw fit: e.g. passing <code class="language-plaintext highlighter-rouge">/type:MyType</code> to
limit it to a single type.</p>

<p>Sgen has historically been flaky to use (as it changed location on disk depending on which Windows SDK was installed 
and some other factors), so it’s great it’s now done via a nuget package. That’s the good part.</p>

<p>The bad part is that since switching to the 
<a href="https://docs.microsoft.com/en-us/dotnet/core/additional-tools/xml-serializer-generator"><code class="language-plaintext highlighter-rouge">Microsoft.XmlSerializer.Generator</code></a> 
nuget package, you’re thinking: “How do I pass arguments to sgen now that it’s automagically invoked via <code class="language-plaintext highlighter-rouge">dotnet build</code>?” 
and also “where is the documentation?”</p>

<p>I had the same reaction, dear reader. Porting an old C# project to <a href="https://dotnet.microsoft.com/download/dotnet/5.0">.NET 5</a> 
resulted in sgen exiting with an error, as the default behaviour tries to generate serializers for every type in the 
target assembly. I don’t want this behaviour for a few reasons:</p>

<ol>
  <li>It generates code I don’t want or need (and a bulkier serialization assembly)</li>
  <li>It fails if namespacing is required (Got an assembly containing <code class="language-plaintext highlighter-rouge">NS1.Triangle</code> &amp; <code class="language-plaintext highlighter-rouge">NS2.Triangle</code>? Sgen will 
fail unless you disambiguate the types via namespacing)</li>
</ol>

<p>We can fix both issues by simply telling sgen “hey, only generate serialization types for <code class="language-plaintext highlighter-rouge">/type:MyType</code>”. Only now we 
can’t, because <code class="language-plaintext highlighter-rouge">Microsoft.XmlSerializer.Generator</code> is calling the shots rather than us directly calling sgen.</p>

<h2 id="the-answer">The answer!</h2>
<p>Blessed art thou, because it wasn’t even possible until 2019 (which seems like an oversight). 
The answer is in the GitHub <a href="https://github.com/dotnet/corefx/pull/36085">Pull Request</a> that added this functionality.</p>

<p>I haven’t been able to find any official documentation, so this is all we have to go on.</p>

<ol>
  <li>Open the .csproj containing the serialization types</li>
  <li>Add a <code class="language-plaintext highlighter-rouge">PropertyGroup</code> section</li>
  <li>Set one of the properties, using the attribute naming format of <code class="language-plaintext highlighter-rouge">&lt;SGenParamName&gt;</code>, where “ParamName” is a parameter name gleaned from the
<a href="https://docs.microsoft.com/en-us/dotnet/standard/serialization/xml-serializer-generator-tool-sgen-exe#parameters">sgen documentation</a>
    <ul>
      <li>E.g. <code class="language-plaintext highlighter-rouge">type</code> would become <code class="language-plaintext highlighter-rouge">&lt;SGenType&gt;</code></li>
    </ul>
  </li>
  <li>Save the project &amp; then build as normal.</li>
</ol>

<p>Here’s the example XML from the <a href="https://github.com/dotnet/corefx/pull/36085">PR</a> by <code class="language-plaintext highlighter-rouge">jiayi11</code> (thank you, kind fellow):</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;SGenReferences&gt;</span>C:\myfolder\abc.dll;C:\myfolder\def.dll<span class="nt">&lt;/SGenReferences&gt;</span>
    <span class="nt">&lt;SGenTypes&gt;</span>SgenTestProgram.MyType1;SgenTestProgram.MyType2<span class="nt">&lt;/SGenTypes&gt;</span>
    <span class="nt">&lt;SGenProxyTypes&gt;</span>false<span class="nt">&lt;/SGenProxyTypes&gt;</span>
    <span class="nt">&lt;SGenVerbose&gt;</span>true<span class="nt">&lt;/SGenVerbose&gt;</span>
    <span class="nt">&lt;SGenKeyFile&gt;</span>mykey.snk<span class="nt">&lt;/SGenKeyFile&gt;</span>
    <span class="nt">&lt;SGenDelaySign&gt;</span>true<span class="nt">&lt;/SGenDelaySign&gt;</span>
<span class="nt">&lt;/PropertyGroup&gt;</span>
</code></pre></div></div>
<p>For my use-case (serialization of a single type in a single project), all I needed to add was:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;SGenTypes&gt;</span>Tools.Blah.MyClass<span class="nt">&lt;/SGenTypes&gt;</span>    
<span class="nt">&lt;/PropertyGroup&gt;</span>
</code></pre></div></div>
<p>That’s it!</p>

<h2 id="a-bonus-tip">A bonus tip</h2>
<p>The documentation for the 
<a href="https://docs.microsoft.com/en-us/dotnet/core/additional-tools/xml-serializer-generator"><code class="language-plaintext highlighter-rouge">Microsoft.XmlSerializer.Generator</code></a> 
doesn’t seem to be 100% up to date for .NET 5 (and .NET 6 is due any day now, too!)</p>

<p>If you’re running with .NET 5, rather than copying the docs that suggest:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package Microsoft.XmlSerializer.Generator <span class="nt">-v</span> 1.0.0
</code></pre></div></div>
<p>&amp;</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;ItemGroup&gt;</span>
   <span class="nt">&lt;DotNetCliToolReference</span> <span class="na">Include=</span><span class="s">"Microsoft.XmlSerializer.Generator"</span> <span class="na">Version=</span><span class="s">"1.0.0"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/ItemGroup&gt;</span>
</code></pre></div></div>

<p>… you can the command &amp; XML version to <code class="language-plaintext highlighter-rouge">=5.0.0</code> to get the latest version.</p>

<h2 id="read-on-for-some-background">Read on for some background</h2>
<p>Anyway, I’ve hopefully saved you some head-banging.</p>

<p>For the rest of you that don’t know what sgen is and are vaguely interested, here’s a short explanation (warning: 
this is boring, but it’s also useful to know for anyone doing XML serialization).</p>

<h2 id="sgenexe">sgen.exe</h2>
<p>Sgen is an XML serialization code generator. For a given assembly and (optionally) a type that you want to serialize, it 
generates a .dll containing the serialization code <strong>at compile-time</strong>.</p>

<p>Let’s say you have a toy project like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>project_root
├── MyApp.sln    
├── MyConsoleApp
│   ├── MyConsoleApp.csproj
│   ├── Program.cs
├── MyClassLibrary
│   ├── MyClassLibrary.csproj
│   ├── NeedsXmlSerialized.cs
</code></pre></div></div>

<p>Our class <code class="language-plaintext highlighter-rouge">NeedsXmlSerialized.cs</code> needs to be serialized to Xml (or deserialized) during the course of the app run.</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">NeedsXmlSerialized</span> 
<span class="p">{</span>
  <span class="k">public</span> <span class="kt">int</span> <span class="n">SomeInt</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In our Program.cs, we use the <code class="language-plaintext highlighter-rouge">XmlSerializer</code> class to convert our <code class="language-plaintext highlighter-rouge">NeedsXmlSerialized</code> instance to XML:</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span> 
<span class="p">{</span>
  <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">()</span> 
  <span class="p">{</span>
    <span class="kt">var</span> <span class="n">serializer</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">XmlSerializer</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">NeedsXmlSerialized</span><span class="p">));</span>
    <span class="k">using</span><span class="p">(</span><span class="kt">var</span> <span class="n">writer</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">StreamWriter</span><span class="p">(</span><span class="s">@"C:\some\file.xml"</span><span class="p">))</span>
    <span class="p">{</span>
      <span class="kt">var</span> <span class="n">typeToBeSerialized</span> <span class="p">=</span> <span class="k">new</span> <span class="n">NeedsXmlSerialized</span><span class="p">;</span>
      <span class="n">typeToBeSerialized</span><span class="p">.</span><span class="n">SomeInt</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span> 
      <span class="n">serializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">writer</span><span class="p">,</span> <span class="n">po</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>OK, so far so good. The program works, and we’ve not even mentioned sgen yet.</p>

<h2 id="if-it-works-already-why-use-sgen">If it works already, why use sgen?</h2>
<p>The short answer is performance. If the serialization types have not yet been generated, they must be created 
on-the-fly at <strong>run-time</strong>. I.e. the first time you want to serialize/deserialize your class to/from XML, there is a 
constant (and large!) stall. The serialization types are then cached &amp; re-used for the rest of the run, but the 
down-payment can be considerable.</p>

<p>I’ve seen this in production and it’s not pretty, especially in pathological cases where the runtime is short-lived and 
only does one bout of serialization, e.g.:</p>
<ul>
  <li>The program starts up (20ms)</li>
  <li>It does some CPU-bound work (300ms)</li>
  <li>XML serialization types are generated on the fly (350ms)</li>
  <li>Serialize to XML (20ms)</li>
  <li>The program exits (5ms)</li>
</ul>

<p>In this case, we spend around half of the program’s runtime generating the serialization types each and every run. This 
is scandalously wasteful. If you were to gaze at the profiling data, you’ll see a monolithic block of waste that seems 
to be non-app code.</p>

<p>An even better thing to do is avoid XML serialization and do something else instead. In my case we have no choice, as 
the file format is sadly non-negotiable.</p>]]></content><author><name>Mark Simpson</name></author><category term="sgen" /><category term="Microsoft.XmlSerializer.Generator" /><category term="dotnet" /><category term="csharp" /><summary type="html"><![CDATA[The problem If you’ve landed on this post via searching the web, you probably already know what sgen.exe is, and what the Microsoft.XmlSerializer.Generator nuget package does.]]></summary></entry></feed>