<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://danclarke.com">
  <title>Dan Clarke</title>
  <subtitle>Software developer, consultant, podcast host, speaker, Dometrain author, community organiser, and Microsoft MVP.</subtitle>
  <link href="https://danclarke.com/rss" rel="self"/>
  <link href="https://danclarke.com/"/>
  <updated>2026-04-24T00:00:00Z</updated>
  <id>https://danclarke.com/</id>
  <author>
    <name>Dan Clarke</name>
    <email>dan@danclarke.com</email>
  </author>
  <entry>
    <title>The Many Use Cases of AI Coding Agents</title>
    <link href="https://danclarke.com/the-many-use-cases-of-ai-coding-agents/"/>
    <updated>2026-04-24T00:00:00Z</updated>
    <id>https://danclarke.com/the-many-use-cases-of-ai-coding-agents/</id>
    <content type="html">&lt;p&gt;I&#39;ve been using AI coding agents &lt;em&gt;a lot&lt;/em&gt; lately - tools like Claude Code, GitHub Copilot CLI, and OpenCode. And the more I use them, the more I keep discovering new use-cases and ways they can be used.&lt;/p&gt;
&lt;p&gt;Most people think of AI coding tools as just that - tools that write code. And yes, they do that, and they do it &lt;em&gt;really&lt;/em&gt; well - but they also do much more than that.&lt;/p&gt;
&lt;h2&gt;Writing Code&lt;/h2&gt;
&lt;p&gt;First, let&#39;s start with writing code, before we dig into other use-cases. Watching the speed at which AI generates code might initially be seen as terrifying. But now, what I&#39;m genuinely finding terrifying - is that I used to write all that myself by hand! The sheer amount of time I spent writing code that just did small, mundane things. Boilerplate, plumbing, wiring things up - all of it. Looking back, it&#39;s crazy how much time that took!&lt;/p&gt;
&lt;p&gt;I rarely write code by hand any more. I describe what I want, and the agent generates the code. And the code it produces is genuinely good - often much better than what I&#39;d have quickly thrown together myself. And I&#39;m also no longer restricted to just languages I know - I recently created a Garmin app for my watch that used &lt;a href=&quot;https://developer.garmin.com/connect-iq/monkey-c/&quot;&gt;Monkey C&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;And when I say &amp;quot;code&amp;quot;, I mean &lt;em&gt;all&lt;/em&gt; of it - application code, infrastructure-as-code, CI/CD pipelines, Dockerfiles, Kubernetes manifests, Terraform, the lot. From the agent&#39;s perspective, there&#39;s no distinction between &amp;quot;application code&amp;quot; and &amp;quot;infrastructure code&amp;quot; - it&#39;s all just text and tools. No more hand-writing YAML! 🎉&lt;/p&gt;
&lt;p&gt;AI also doesn&#39;t have the mental resistance that a human has - for example, when faced with various time and business pressures, it&#39;s common for a human developer to skip &amp;quot;just a few&amp;quot; of those automated test cases. &amp;quot;Let me just write enough test coverage to get it through the PR review&amp;quot; (sound familiar?). AI doesn&#39;t have that constraint - it can generate a lot of test coverage without the mental fatigue / resistance, and also in a fraction of the time. Why wouldn&#39;t you take advantage of that?&lt;/p&gt;
&lt;h2&gt;The Agentic Loop&lt;/h2&gt;
&lt;p&gt;There&#39;s a concept that underpins a lot of what makes AI coding agents so effective, and it&#39;s worth calling out explicitly - the &lt;em&gt;agentic loop&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;When an AI agent is working on a task, it doesn&#39;t just have one go and hand you the result. It tries something, then &lt;em&gt;verifies&lt;/em&gt; its own work. If the build fails, it reads the errors and fixes them. If the tests fail, it looks at the failures and iterates. It keeps going round this loop - try, verify, fix, repeat - until the task is done.&lt;/p&gt;
&lt;p&gt;Using AI is no longer &amp;quot;type a prompt into a textbox, then manually copy code snippets out of the response&amp;quot; - then the typical back and forth, complaining about AI hallucinating. Hallucination is still a thing - but we just see less of it thanks to this hands-off self-correcting agentic loop.&lt;/p&gt;
&lt;h2&gt;Calling CLI Tools You Already Have&lt;/h2&gt;
&lt;p&gt;This one is &lt;em&gt;huge&lt;/em&gt;, and I think it&#39;s massively underappreciated.&lt;/p&gt;
&lt;p&gt;Your machine is already full of powerful command-line tools - &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;az&lt;/code&gt;, &lt;code&gt;dotnet&lt;/code&gt;, &lt;code&gt;terraform&lt;/code&gt;, PowerShell, the Aspire CLI, and countless others. You probably use a handful of them regularly, but let&#39;s be honest - how many of us actually remember the syntax for anything beyond the basics? And even if you do - it takes time to type - so you&#39;re focusing on that, rather than what you&#39;re actually trying to achieve.&lt;/p&gt;
&lt;p&gt;AI coding agents have access to those commands, and for most of them they&#39;ll already know all the syntax and tricks anyway. If it doesn&#39;t - it can quickly find out. Then it can just &lt;em&gt;run&lt;/em&gt; these tools directly for you - they become an implementation detail, rather than something you have to worry about. You don&#39;t need to remember the syntax - you just tell the agent what you want to achieve, and it figures out which commands to run.&lt;/p&gt;
&lt;p&gt;A few weeks ago, my Kubernetes cluster was reaching capacity. Rather than manually inspecting deployments and working out what was over-provisioned, I just told Claude Code to use &lt;code&gt;kubectl&lt;/code&gt; to explore the cluster and see if anything could be optimised. I also pointed it at the code for the projects I was hosting. It inspected resource requests/limits across all my deployments, identified things that were over-provisioned, and made a bunch of suggestions to clean things up. The whole process would have taken me ages - it took the agent minutes. It also meant I didn&#39;t need to increase my cluster&#39;s capacity - so immediately saved me money.&lt;/p&gt;
&lt;p&gt;Similarly, the &lt;a href=&quot;https://aspire.dev/&quot;&gt;Aspire&lt;/a&gt; team have recently ramped up what the Aspire CLI can do - it can now pull logs, traces, and metrics. This means your AI coding agent can just call the Aspire CLI to get all of that observability data. Combine that with the fact that the agent also has access to your &lt;em&gt;code&lt;/em&gt;, and it can connect the dots between what&#39;s happening at runtime and what&#39;s in the codebase. That&#39;s incredibly powerful.&lt;/p&gt;
&lt;h2&gt;Debugging and Investigating Issues&lt;/h2&gt;
&lt;p&gt;Because the agent has access to logs, traces, and metrics - whether that&#39;s via the Aspire CLI, a Grafana MCP, or just reading log files - &lt;em&gt;and&lt;/em&gt; it also has access to the code itself, it can investigate issues far faster than I can. It can trace a problem from the symptoms in the logs right through to the offending line of code, all in one flow.&lt;/p&gt;
&lt;p&gt;But it&#39;s not just code bugs. A few weeks ago, my machine was running slowly. I couldn&#39;t figure out why. So I told Claude Code to investigate it. The agent used a whole bunch of command-line tools to look at event logs and system diagnostics. It found that my network driver was outdated and was raising a huge number of error events, which was dragging the whole system down. I would &lt;em&gt;never&lt;/em&gt; have found that myself - at least not without a lot of frustration and wasted time.&lt;/p&gt;
&lt;p&gt;Same goes for CI pipeline failures - I quite often just give the build id to the agent and tell it to investigate and fix the issue. It&#39;ll grab the build logs from either the &lt;code&gt;gh&lt;/code&gt; CLI if in GitHub Actions, or the Azure DevOps MCP if in Azure DevOps (those are the two I use) - and do the rest itself!&lt;/p&gt;
&lt;h2&gt;Throwaway Scripts&lt;/h2&gt;
&lt;p&gt;There are tasks that come up from time to time where you &lt;em&gt;could&lt;/em&gt; write a script to automate it, but the effort of writing, testing, and debugging that script just isn&#39;t worth it for a one-off job. So you end up doing it manually, which is tedious and error-prone.&lt;/p&gt;
&lt;p&gt;AI agents completely change that. They can knock out a throwaway script in seconds, use it, and then you just delete it.&lt;/p&gt;
&lt;p&gt;Here&#39;s a real example: I wanted to export my newsletters from Beehiiv and convert them into Markdown files so they could be source-controlled. The Beehiiv export gives you CSVs full of HTML - completely unreadable. I told Claude Code to take the CSV and convert all the newsletter issues into individual Markdown files. I also told it to use the Playwright CLI to verify that the converted output matched the original.&lt;/p&gt;
&lt;p&gt;The agent then created a Python script full of regular expressions to bulk-convert the HTML. It ran Playwright to verify the output, found mistakes, went back and modified the Python script, and kept iterating until everything matched up perfectly - all without my involvement. Once it was done, we just deleted the script. Job done.&lt;/p&gt;
&lt;p&gt;In the past, writing that script by hand - with all the edge cases and verification - just wouldn&#39;t have been an option. But with an AI agent, I&#39;m now able to do things that I previously couldn&#39;t have justified spending the time on.&lt;/p&gt;
&lt;h2&gt;Custom Skills and Commands&lt;/h2&gt;
&lt;p&gt;Most AI coding agents support some form of custom skills or commands - reusable prompts that you can trigger with a shortcut. In Claude Code, for example, you can create skills and invoke them with a slash command.&lt;/p&gt;
&lt;p&gt;This has been a game-changer for me. Here are a few I use regularly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Podcast show notes&lt;/strong&gt; - I&#39;ve got a skill that creates new show notes for my podcast, following my exact format and structure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Newsletter editions&lt;/strong&gt; - A skill that creates a new newsletter edition and researches news items for me. (note that I still manually cherry pick what news items I want, and more often than not, hand-write the descriptions - &lt;a href=&quot;https://danclarke.beehiiv.com/?utm_source=danclarke.com&amp;amp;utm_medium=referral&quot;&gt;my newsletter&lt;/a&gt; is certainly not AI slop!)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git commits&lt;/strong&gt; - When I run &lt;code&gt;/commit&lt;/code&gt;, it asks me if I want to include &amp;quot;Closes #123&amp;quot; for a GitHub issue, and whether I want to push afterwards. It&#39;s a small thing, but it&#39;s &lt;em&gt;exactly&lt;/em&gt; how I want my workflow to work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jira tickets&lt;/strong&gt; - I&#39;ve got a skill that creates Jira tickets and fills out all the custom fields that my client requires, asking me questions along the way. No more fighting with Jira&#39;s UI, and remembering that client&#39;s various custom fields I need to set.&lt;/li&gt;
&lt;li&gt;Plus many others.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating these skills is surprisingly easy - it&#39;s just a directory with a markdown file called &lt;code&gt;SKILL.md&lt;/code&gt;, plus any other additional files you want to include (images, PDFs, etc). Claude Code even has a skill creator skill that helps you build, improve, and verify them. If you don&#39;t use Claude Code, you can still use &lt;a href=&quot;https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md&quot;&gt;that skill&lt;/a&gt; - as skills are just portable markdown, following a standard format.&lt;/p&gt;
&lt;h2&gt;Tickets, PRs, and Collaboration Tools&lt;/h2&gt;
&lt;p&gt;Thanks to tools like the GitHub CLI (&lt;code&gt;gh&lt;/code&gt;), the Azure DevOps MCP server, and similar - your agent can read and write across all the collaboration tools you use day-to-day. Tickets (JIRA, Azure DevOps, GitHub Issues, etc), PR comments, the lot.&lt;/p&gt;
&lt;p&gt;I often get the agent to query a ticket to pull out the description and acceptance criteria before starting work. It&#39;s an amazing context primer, and gives the agent a good checklist to work against. As mentioned above, I also use a skill that writes JIRA tickets for me.&lt;/p&gt;
&lt;p&gt;On the PR side, the agent can read comments that reviewers have left and either fix them directly, or suggest reasons to push back. Technically, you can also get it to reply in the comments for you - but I probably wouldn&#39;t recommend that if the reviewer is expecting a human response. Unless of course it&#39;s an accepted practice within the team.&lt;/p&gt;
&lt;p&gt;As an aside, GitHub does have native cloud-based AI code reviews too - but those are out of scope/context of this post.&lt;/p&gt;
&lt;h2&gt;Standard disclaimer!&lt;/h2&gt;
&lt;p&gt;Before you go and let your AI agent loose pointing at all your production environments - obviously ensure you take the necessary precautions. If you&#39;re giving it access to a prod Kubernetes cluster for example - ensure its authentication has the necessary authorisation restrictions - eg. readonly.&lt;/p&gt;
&lt;p&gt;It&#39;s easy to anthropomorphise these AI agents - and yet, people are happy to only anthropomorphise certain traits, and still expect AI to not make mistakes. AI does make mistakes, just like humans do. The problem is that if AI makes mistakes - it makes them MUCH FASTER. So do be careful.&lt;/p&gt;
&lt;h2&gt;Personal projects&lt;/h2&gt;
&lt;p&gt;On a personal note, I&#39;m currently using Claude Code to build lots of stuff that I wouldn&#39;t have previously been able to justify...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An entire fitness product that I&#39;m planning to productise. Before AI, this project would have needed a team of developers doing years of work. Now I&#39;m able to build it in my spare time without having to quit my job, or it impacting my family-time or fitness training. That&#39;s how much of a difference this makes.&lt;/li&gt;
&lt;li&gt;Plus so many small utility apps - from a &lt;a href=&quot;https://github.com/dracan/tomato&quot;&gt;desktop pomodoro timer&lt;/a&gt;, to a &lt;a href=&quot;https://github.com/dracan/timeblock-app&quot;&gt;time blocking app&lt;/a&gt;. Things I never would have been able to justify the time creating previously.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, lots of non-code stuff - a few examples...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I coach kids&#39; athletics and have recently been managing a series of competitions that our club was competing in. This is a real pain to do, as there are various activities with limited slots, and I get various requests from parents. There&#39;s also a bunch of forms to fill in, etc. Claude Code has made this WAY easier - especially working out the allocations, which can get complicated.&lt;/li&gt;
&lt;li&gt;I wanted a personal-finance long-term forecast solution - which takes into account income, pensions (including future draw-down over time), inflation, future big spends, etc, etc. I just threw all my info into Claude Code, and it generated a system for me, which shows charts and projections, doing all the tax calculations, etc. It&#39;s amazing!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus many more things that I won&#39;t bore you with - but the point is, the use-cases for AI coding agents go way beyond just writing code.&lt;/p&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;I&#39;ve covered a lot of use cases here, but honestly, this barely scratches the surface. Every week I seem to discover a new use-case that I can use an AI agent for. The combination of being able to write code, run any CLI tool on your machine (or use MCP Servers), generate throwaway scripts, and iterate on their own work through the agentic loop - it all adds up to something that totally changes the way we work.&lt;/p&gt;
&lt;p&gt;I should probably briefly touch on cost. Personally, I pay for the Claude Max 5x plan. It&#39;s not cheap if you compare it to a subscription like Netflix or Spotify. But if you compare it to having a team of developers at your disposal, it&#39;s an absolute bargain. The amount of time it saves me, and the amount of stuff it enables me to build - both personally and professionally - is worth way more than the cost.&lt;/p&gt;
&lt;p&gt;If you&#39;re using AI coding tools but only for writing code, I&#39;d really encourage you to explore what else they can do. I&#39;d love to hear about the different use-cases you&#39;re using AI for! Please let me and the other readers know in the comments below! 👇&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Developers are System Thinkers, not Code Monkeys</title>
    <link href="https://danclarke.com/developers-are-system-thinkers-not-code-monkeys/"/>
    <updated>2026-04-11T00:00:00Z</updated>
    <id>https://danclarke.com/developers-are-system-thinkers-not-code-monkeys/</id>
    <content type="html">&lt;p&gt;There&#39;s &lt;em&gt;a lot&lt;/em&gt; of concern amongst developers at the moment that AI is going to take our jobs. And I get it - things are moving &lt;em&gt;fast&lt;/em&gt;. But I think a lot of this fear comes from a misunderstanding of what we actually do. We&#39;re not typists. We&#39;re not &amp;quot;code monkeys&amp;quot;. We are system thinkers.&lt;/p&gt;
&lt;p&gt;First of all though - I&#39;m going to be blunt. If you&#39;re not &lt;em&gt;heavily&lt;/em&gt; leveraging AI right now - learning it, playing with it, embracing it - then you&#39;re going to get left behind. And not just by other developers - by &lt;em&gt;non-developers&lt;/em&gt; who are! That should be a wake-up call.&lt;/p&gt;
&lt;p&gt;Right, let&#39;s talk about systems. As developers, we are &lt;em&gt;very&lt;/em&gt; good at systems. Think about it - we work with them &lt;em&gt;all day long&lt;/em&gt;...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your code is a system (consisting of functions, classes, modules, and so on)&lt;/li&gt;
&lt;li&gt;A database is a system&lt;/li&gt;
&lt;li&gt;A message broker is a system&lt;/li&gt;
&lt;li&gt;Your CI/CD pipeline is a system&lt;/li&gt;
&lt;li&gt;Git is a system&lt;/li&gt;
&lt;li&gt;JIRA is a system&lt;/li&gt;
&lt;li&gt;Those automation scripts you wrote to save yourself time? Systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And all of these things combine together in different ways to make &lt;em&gt;bigger&lt;/em&gt; systems.&lt;/p&gt;
&lt;p&gt;We&#39;ve spent our entire careers taking complex problems, breaking them down into smaller pieces, and building systems to solve them. We understand how to integrate different components together. We understand how things fit together.&lt;/p&gt;
&lt;p&gt;Make no mistake - AI is going to &lt;em&gt;dramatically&lt;/em&gt; change our industry. It already is. The way we build software, the way we work, the tools we use - none of this is going to look the same in a few years. But at its core, AI is still a system. A &lt;em&gt;very&lt;/em&gt; powerful system that&#39;s evolving at a frightening pace - but still a system. And who better to understand, integrate, and leverage a new system than the people who&#39;ve been doing exactly that for years?&lt;/p&gt;
&lt;p&gt;The developers who will thrive are the ones who recognise that &lt;em&gt;this&lt;/em&gt; is their real skill. Not typing code - but understanding systems and how to orchestrate them. If you can design how an AI agent fits into a larger architecture, break down what it should and shouldn&#39;t do, and integrate it alongside your existing systems - you&#39;re in a &lt;em&gt;really&lt;/em&gt; strong position.&lt;/p&gt;
&lt;p&gt;So don&#39;t be the developer who&#39;s afraid of AI. Be the developer who treats it as the latest system to learn. Play with it. Experiment. Build things with it. The more you understand how it works, the better you&#39;ll be at leveraging it - and that&#39;s &lt;em&gt;exactly&lt;/em&gt; what we&#39;ve always done.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Year in Review: 2025</title>
    <link href="https://danclarke.com/year-in-review-2025/"/>
    <updated>2026-01-02T00:00:00Z</updated>
    <id>https://danclarke.com/year-in-review-2025/</id>
    <content type="html">&lt;p&gt;Happy New Year everyone! 🎉&lt;/p&gt;
&lt;p&gt;Let&#39;s start off with a few small confessions...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s been quite a few years since my last blog post!🙈&lt;/li&gt;
&lt;li&gt;I intended to get more into my YouTube channel, but didn&#39;t publish a single video! 😬 (though I did publish one on Nick Chapsas&#39;s channel! - more on that later)&lt;/li&gt;
&lt;li&gt;I only published 10 podcast episodes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first two are definitely on my list of new year&#39;s resolutions to do something about in 2026. The podcast one too - but, to be honest, I&#39;m happy if I do one a month - it&#39;s the YouTube channel I want to focus more on this year.&lt;/p&gt;
&lt;p&gt;For the blog - one of the things I did do in 2025, was a re-write of this website! (see below). So given that, I should really start getting back into blogging! So let&#39;s start that new years resolution with another &amp;quot;year in review&amp;quot; post...&lt;/p&gt;
&lt;h1&gt;Website re-write&lt;/h1&gt;
&lt;p&gt;I wanted to move this website to a be a statically-generated site (not using a database, etc). This makes it more manageable, and all in one place. I went with &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also wanted to change the home page to be a place that I can send people to for links to all my things (social links, podcast, newsletter, courses, etc).&lt;/p&gt;
&lt;p&gt;I&#39;m really pleased with how it&#39;s turned out - and AI certainly made the rewrite a lot easier too! If you have any thoughts or feedback, please do let me know.&lt;/p&gt;
&lt;h1&gt;Podcast&lt;/h1&gt;
&lt;p&gt;There were 10 podcast episodes released this year. Which doesn&#39;t actually sound that much when I look at that number! I would have guessed more than that. There were 20k downloads in 2025. I had some really amazing guests on - checkout the website for more details: &lt;a href=&quot;https://unhandledexceptionpodcast.com/&quot;&gt;unhandledexceptionpodcast.com&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Newsletter&lt;/h1&gt;
&lt;p&gt;My newsletter has continued to go out each month - with a steady growth in subscribers. Each episode has a list of hand-picked new items from the past month, a dev tip, a dev comic pick, and more.&lt;/p&gt;
&lt;p&gt;I must admit, whilst the number of subscribers is growing over time - it&#39;s not growing as much as I would have hoped. Which I find a bit surprising, given the news items are such a great way for developers to keep up to date with the latest happenings in our industry. And it&#39;s only monthly, so not a big inbox hit either.&lt;/p&gt;
&lt;p&gt;If you haven&#39;t signed up yet, you can do so here: &lt;a href=&quot;https://danclarke.beehiiv.com/?utm_source=danclarke.com&amp;amp;utm_medium=referral&amp;amp;utm_campaign=year-in-review-2025&quot;&gt;danclarke.beehiiv.com&lt;/a&gt;. If you &lt;em&gt;have&lt;/em&gt; signed up - thank you! I really appreciate it. If you get value from it, please do share it with your friends and colleagues - it really does help.&lt;/p&gt;
&lt;h1&gt;Everstack&lt;/h1&gt;
&lt;p&gt;With my company, &lt;a href=&quot;https://www.everstack.com/&quot;&gt;Everstack&lt;/a&gt; - I&#39;ve continued working with my current two main clients. One of which, I&#39;ve finally got to the end of the backlog, which will free up a bit of my time for more content creation, which I really want to focus more on in 2026.&lt;/p&gt;
&lt;h1&gt;Dometrain&lt;/h1&gt;
&lt;p&gt;Earlier in 2025, I released my 4th course on Dometrain - &lt;a href=&quot;https://dometrain.com/course/getting-started-dotnet-aspire/?ref=dan-clarke&amp;amp;promotion=danclarkedotcom&quot;&gt;Getting Started with .NET Aspire&lt;/a&gt;. This was a really fun course to create, and I&#39;ve had some great feedback. I&#39;m strongly aware that the technology has moved on a bit since I created the course, and I really want to update it in 2026 to reflect the latest changes.&lt;/p&gt;
&lt;p&gt;The previous courses (&lt;a href=&quot;https://dometrain.com/course/from-zero-to-hero-docker-for-developers/?ref=dan-clarke&amp;amp;promotion=danclarkedotcom&quot;&gt;Docker&lt;/a&gt;, &lt;a href=&quot;https://dometrain.com/course/from-zero-to-hero-kubernetes-for-developers/?ref=dan-clarke&amp;amp;promotion=danclarkedotcom&quot;&gt;Kubernetes&lt;/a&gt;, and &lt;a href=&quot;https://dometrain.com/course/from-zero-to-hero-jetbrains-rider/?ref=dan-clarke&amp;amp;promotion=danclarkedotcom&quot;&gt;JetBrains Rider&lt;/a&gt;) have all continued to do well, and I&#39;ve had some great feedback from those too.&lt;/p&gt;
&lt;h1&gt;YouTube&lt;/h1&gt;
&lt;p&gt;Each time I finish a course, which gives me tons of practice with creating videos - I say to myself, &amp;quot;right, now I&#39;m all warmed up with video creation, I must do more on YouTube!&amp;quot;. But then I never seem to get around to it! So 2026 is the year I really want to focus more on my YouTube channel. If you can all be my accountability partners and nag me if I don&#39;t - that would be amazing! 😄&lt;/p&gt;
&lt;p&gt;*In fact - at the start of this post, when I said I didn&#39;t publish a single YouTube video. That isn&#39;t technically correct, as I did have the honour of being asked by Nick Chapsas to publish a video on his channel! I created one on &lt;a href=&quot;https://www.youtube.com/watch?v=DpyjAKmNwpI&quot;&gt;Getting Started with MCP (Model Context Protocol)&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;.NET Oxford&lt;/h1&gt;
&lt;p&gt;We had three in-person .NET Oxford meetups in 2025, and it was great to catch up with everyone again. Our meetups are quarterly, and we unfortunately missed the first (March) one due to me being a bit rubbish. The June and December ones were our usual lightning talk format, and for the October one we held an &amp;quot;unconference&amp;quot;, which is no set agenda, no prepared talks, no slides. We used &lt;a href=&quot;https://www.slido.com/&quot;&gt;Slido&lt;/a&gt; to gather topics from the attendees, and then everyone voted on what to discuss. We picked the top ones, and time-boxed group discussions about them. It was really nice doing that rather than our usual &amp;quot;speaker in front of everyone just watching&amp;quot; format. We&#39;ll definitely have to try that another time.&lt;/p&gt;
&lt;h1&gt;Personal life&lt;/h1&gt;
&lt;p&gt;On the personal side of things, 2025 was a really good year. My family and I went on a few different holidays, and have already planned more for this year. My boys are growing up so fast now - my eldest is almost as tall as me! I feel incredibly lucky and fortunate to have such an amazing family, a job I love, and great friends.&lt;/p&gt;
&lt;h1&gt;New Years Resolutions&lt;/h1&gt;
&lt;p&gt;To wrap up this post, here&#39;s a list of my new years resolutions (some mentioned above).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get back into creating content on my YouTube channel&lt;/li&gt;
&lt;li&gt;Start blogging again&lt;/li&gt;
&lt;li&gt;Start playing the guitar again&lt;/li&gt;
&lt;li&gt;Get into running (I train a lot at the gym with weights, but am really unfit cardio-wise)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I plan to continue as I am, for other areas - eg. podcast, gym, family, client-work, etc. So whilst not technically &#39;new years resolutions&#39; - they&#39;re definitely things I need to continue focusing on.&lt;/p&gt;
&lt;p&gt;I&#39;m also very excited to see what happens in 2026 with AI. There was so much progress in 2025, and I can&#39;t see it stopping soon. We certainly have interesting times coming up, and I for one, am so glad to get to see this within my lifetime!&lt;/p&gt;
&lt;p&gt;Happy New Year everyone, and happy coding (vibing?)!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Source-controlling Config files in Git (dotfiles?)</title>
    <link href="https://danclarke.com/config-files-in-git/"/>
    <updated>2022-10-08T00:00:00Z</updated>
    <id>https://danclarke.com/config-files-in-git/</id>
    <content type="html">&lt;p&gt;I&#39;ve recently setup some of my various config files to be source-controlled in a Github repository. Here are some examples of what I mean by config files...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My .gitconfig file - which includes various aliases and settings&lt;/li&gt;
&lt;li&gt;My Powershell profile - which likewise, includes various aliases and environment variables, etc&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ohmyposh.dev/&quot;&gt;OhMyPosh&lt;/a&gt; theme - customised command line setup (see screenshot below)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/terminal&quot;&gt;Windows Terminal&lt;/a&gt; config&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey&lt;/a&gt; packages&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neovim.io/&quot;&gt;NeoVim&lt;/a&gt; config files&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://k9scli.io/&quot;&gt;K9s&lt;/a&gt; config&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2022-10-DotFiles/wt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are more configs I want to add - eg. my &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; config files; my &lt;a href=&quot;https://www.jetbrains.com/rider/&quot;&gt;Rider&lt;/a&gt; config, etc.&lt;/p&gt;
&lt;h1&gt;Why?&lt;/h1&gt;
&lt;p&gt;I&#39;ve found a ton of benefits to doing this, which I&#39;ll try and detail in this post...&lt;/p&gt;
&lt;h2&gt;Confidence in not losing my setting&lt;/h2&gt;
&lt;p&gt;In the past when I&#39;ve done things like customise my terminal - I would have no idea how to get it back exactly as it was if I lost all my settings. Even with my .gitconfig setup - there are aliases in there which I&#39;ve fine-tuned over time, which I really wouldn&#39;t like to lose! And Vim... ha! Any Vim user who loses their config - good luck recreating that if you&#39;ve been tweaking over time! Having all of these things in Github backed up takes a massive weight off my mind.&lt;/p&gt;
&lt;h2&gt;My changes are intentional and deliberate&lt;/h2&gt;
&lt;p&gt;Every time I tweak a configuration - I create an explicit commit for that change with a meaningful commit message. No changes are accidentally made, and I&#39;m free to play with my configuration temporarily. I can see those changes in my pending changes, and choose whether to revert or make them official with a commit.&lt;/p&gt;
&lt;h2&gt;Increased motivation to tweak&lt;/h2&gt;
&lt;p&gt;Because this is a Github repository that contains all of my config files - it&#39;s now become quite important to me. They&#39;re not just random individual config files on my computer any more. Because of the above two points (confidence and intentional changes) - I&#39;ve found that I&#39;m now much more motivated to improve them and make more tweaks and customisations, to get an even more awesome setup!&lt;/p&gt;
&lt;h2&gt;Transferable between machines&lt;/h2&gt;
&lt;p&gt;Another obvious advantage is that I can share them between multiple machines. I have a desktop, laptop, and multiple VMs. If I make a tweak on one machine - I&#39;ll push those changes, and periodically pull when I&#39;m on the another machines.&lt;/p&gt;
&lt;h2&gt;Github issues!&lt;/h2&gt;
&lt;p&gt;Because this is a Github repo, I can create issues against it. Quite often I&#39;ll be in the middle of something, and have an idea for a config tweak I&#39;d like to do to improve my workflow - but I don&#39;t have time to do it at that moment. Eg. I mentioned earlier that I&#39;d like to add VSCode and Rider config to it. Or it might be Vim tweak that I&#39;ll need to look up how to do. Github issues are perfect for capturing those ideas, so I don&#39;t forget!&lt;/p&gt;
&lt;h1&gt;How does it work?&lt;/h1&gt;
&lt;p&gt;Obviously each application has its config files in different locations - whereas a Git repo is just in one place. Symlinks solve this problem nicely! I have a Powershell script called &lt;code&gt;Setup.ps1&lt;/code&gt; in the same repository, which ensures my symlinks are all setup (code is below). The script is idempotent, so I can run it anytime I want to make sure my system is up to date with any symlink changes I might have made on another of my machines.&lt;/p&gt;
&lt;p&gt;Then I have folders for each app...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2022-10-DotFiles/vscode.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To pre-empt the obvious question of whether my repo is public - I&#39;m afraid, it&#39;s not. I don&#39;t want to have to worry about what I push to it. Mostly, there&#39;s nothing sensitive in there - but there are a few directory aliases that point to client folders which include their project names. It&#39;s just easier to keep it private.&lt;/p&gt;
&lt;p&gt;However, here&#39;s a Gist with my Setup.ps1 in it to get you started...&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/dracan/658c16064c662695a498b6c6e0507b81.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;The above script and the folder structure in the screenshot above, should give you a good starting point and idea of how it works, for your own config files.&lt;/p&gt;
&lt;h1&gt;What are &#39;dotfiles&#39;?&lt;/h1&gt;
&lt;p&gt;After I started doing this - I was talking about it with someone, and they asked if I was using &#39;dotfiles&#39;. I had heard the term, but wasn&#39;t sure what it was. So I Googled it, and found &lt;a href=&quot;https://dotfiles.github.io/&quot;&gt;this&lt;/a&gt; and a few blog posts. The name comes from the fact that quite a few config files start with a dot - eg. &lt;code&gt;.gitconfig&lt;/code&gt;, &lt;code&gt;.vimrc&lt;/code&gt;, etc. Whilst there seems to be a community around creating helper scripts around &#39;dotfiles&#39; - from what I gather - it looks like this isn&#39;t an actual &lt;em&gt;thing&lt;/em&gt; - but rather just a term for what I was already doing anyway. Ie. Leveraging symlinks to make config files source-controllable.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Who should mark PR comments as resolved?</title>
    <link href="https://danclarke.com/resolving-pr-comments/"/>
    <updated>2022-05-27T00:00:00Z</updated>
    <id>https://danclarke.com/resolving-pr-comments/</id>
    <content type="html">&lt;p&gt;Before reading this post - note that when I say &amp;quot;resolving&amp;quot;, I&#39;m &lt;em&gt;only&lt;/em&gt; talking about clicking the &amp;quot;Resolve&amp;quot; button for that particular comment thread. I am &lt;em&gt;not&lt;/em&gt; talking about actually fixing the issue that the comment is describing.&lt;/p&gt;
&lt;p&gt;The TLDR; version is that the person who started the PR comment should be the person to mark it as resolved.&lt;/p&gt;
&lt;p&gt;Also note that I&#39;m talking about PRs within a team - not open source projects.&lt;/p&gt;
&lt;p&gt;The reason I&#39;m writing this post, is that it&#39;s something I keep on seeing come up. I leave a PR comment, and the PR author makes a change to fix it, then they resolve my comment before I have a chance to review that fix/change.&lt;/p&gt;
&lt;p&gt;What&#39;s wrong with this? It means changes can be merged into the main branch that haven&#39;t been reviewed at all. Which kindof defeats the purpose of a PR!&lt;/p&gt;
&lt;p&gt;Take this scenario...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bob creates a PR for a feature he&#39;s been working on&lt;/li&gt;
&lt;li&gt;Both Frank and Jane are reviewing the PR&lt;/li&gt;
&lt;li&gt;Jane spots a bug, and adds a comment&lt;/li&gt;
&lt;li&gt;Frank doesn&#39;t spot the bug and approves the PR&lt;/li&gt;
&lt;li&gt;Bob fixes the bug, marks Jane&#39;s comment about the bug as resolved, and then completes the PR&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, Bob misunderstood the bug fix, and didn&#39;t fix it correctly. Jane, who spotted the bug hasn&#39;t had chance to review/accept the new changes, as Bob wrongly completed the PR comment on her behalf. Unreviewed code has now made it into the main branch, which could well be CI/CDing straight to production!&lt;/p&gt;
&lt;p&gt;If the author of the PR comment is always the person who has the responsibility of marking that comment as resolved - this gets rid of this problem, and all changes will have had by at least two people&#39;s eyes on them.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Redefining the term &#39;10x Developer&#39;</title>
    <link href="https://danclarke.com/redefining-the-term-10x-developer/"/>
    <updated>2022-05-12T00:00:00Z</updated>
    <id>https://danclarke.com/redefining-the-term-10x-developer/</id>
    <content type="html">&lt;p&gt;TLDR; - The term &amp;quot;100x Develop&lt;em&gt;&lt;strong&gt;ment&lt;/strong&gt;&lt;/em&gt;&amp;quot; is better because it focuses more on the nature of software development, not an individual. And the 10x is just too small of a multipler.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I was recently honoured to be a guest on &lt;a href=&quot;https://dotnetcore.show/&quot;&gt;The .NET Core Podcast&lt;/a&gt; to chat with &lt;a href=&quot;https://twitter.com/podcasterJay&quot;&gt;Jamie Taylor&lt;/a&gt; about developer productivity. The full episode can &lt;a href=&quot;https://dotnetcore.show/episode-97-developer-productivity-with-dan-clarke/&quot;&gt;be found here&lt;/a&gt;, or in your favourite podcast app.&lt;/p&gt;
&lt;p&gt;In it, I asked Jamie what he thought of the term &amp;quot;10x developer&amp;quot; - which is a term I hear thrown around quite a lot. Here was his reply...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;If I&#39;m honest, right hand on heart, I hate the phrase. 10x when compared to what, right? You can&#39;t say 10x when you haven&#39;t described what a 1x is, right? I think that is used by people to sort of say I&#39;m better than you. And in a team, you don&#39;t want a superstar, you want a bunch of people who are all working together. And so if I was working in a team with someone who was a self-proclaimed 10x developer, I would try to avoid them as much as possible.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I found this a really interesting angle, and as I thought about it afterwards - I realised that the problem with the term, is the word &amp;quot;developer&amp;quot;. Okay, that&#39;s not the only problem - eg. productivity isn&#39;t a linear sliding scale, so what does 10x even relate to - but let&#39;s ignore that for now.&lt;/p&gt;
&lt;p&gt;The thing I do like about the term is the 10x bit. Or 100x. Or 1000x. And I&#39;m not talking about an individual developer being 10x either. I&#39;m talking about the nature of tasks in our industry being 10/100/1000x depending on how you go about that said task.&lt;/p&gt;
&lt;p&gt;Here are a few examples...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need to fixup namespaces across your entire solution. Do you do it manually in each file? Or do you hit a keyboard shortcut so your IDE does it for you in seconds? How many X is that? 100+ maybe?&lt;/li&gt;
&lt;li&gt;You spend 2 months building a feature. Or you discover a library that does it for you. 2 months vs 1 hour? 300x.&lt;/li&gt;
&lt;li&gt;You have a process that&#39;s repeatable and needs doing often. You manually do this process each day for the next few years. Or you automate it to get all that time back. 1000x?&lt;/li&gt;
&lt;li&gt;You have a performance issue in your app. You manually try to work it out by putting logs and timers in code. Or you&#39;ve previously learnt how to use a profiling tool which very quickly gives you a detailed analysis of what&#39;s going on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see - in software development, there are massive multipliers to be had depending on how a task is done. A developer who knows more of these things, will be more productive - but I think the term &amp;quot;10x developer&amp;quot; focuses on the wrong thing. It focused on the individual, rather than the tasks themselves. So I think &lt;em&gt;&amp;quot;10x development&amp;quot;&lt;/em&gt; is a much better term, which focuses more on the act of software development, rather than an individual. The &amp;quot;10&amp;quot; bit could perhaps be changed too, as it can be way more than that. So let&#39;s change that to 100x - as the point is that it highlights the very high standard-deviation in time taken to complete a task based on how that task is done.&lt;/p&gt;
&lt;p&gt;Basically, work smarter to be more productive. I don&#39;t know where the term &lt;em&gt;&amp;quot;work smarter, not harder&amp;quot;&lt;/em&gt; originally came from - but if there&#39;s one industry where this applies to more than any other - it&#39;s software development. As developers, we have so much opportunity and power to automate tasks, leverage IDEs and tooling, and complete tasks WAY faster.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Raising Awareness of Aphantasia</title>
    <link href="https://danclarke.com/aphantasia/"/>
    <updated>2022-01-21T00:00:00Z</updated>
    <id>https://danclarke.com/aphantasia/</id>
    <content type="html">&lt;p&gt;Last year, whilst browsing the web on my phone, I learnt something that blew my mind. I can&#39;t remember exactly what (or why) I googled about &#39;visualisations&#39;, but some of the results said things like &lt;em&gt;&amp;quot;Did you know that some people can&#39;t see images in their mind&#39;s eye?!&amp;quot;&lt;/em&gt;. I read that, and thought - &amp;quot;Wait? What! Some people &lt;em&gt;can&lt;/em&gt;?!&amp;quot;.&lt;/p&gt;
&lt;p&gt;So it turns out that ~3-5% of the population have something called Aphantasia, which means they have a blind mind&#39;s eye. There&#39;s also some research that suggests that people with Alphantasia are more likely to get into a career in IT - meaning in our industry, the percentage could well be higher. So this post isn&#39;t &lt;em&gt;completely&lt;/em&gt; off-topic!&lt;/p&gt;
&lt;p&gt;Based on those stats - most of you reading this will be surprised that some people can&#39;t see things in their minds eye...&lt;/p&gt;
&lt;p&gt;But &lt;em&gt;some&lt;/em&gt; of you will be reading this, and be as blown away as I was. After all - how we percieve our thoughts isn&#39;t something that crops up in every-day converstion. I&#39;m in my 40s, and have only just learnt that other people can see images in their heads. There must be so many people out there who, like me, have Aphantasia but don&#39;t know it. This is the purpose of this blog post - to raise awareness of it. I&#39;d love to hear your experiences in the comments - &lt;em&gt;especially&lt;/em&gt; if you&#39;ve only just discovered you have Aphantasia!&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&quot;https://unhandledexceptionpodcast.com/posts/0028-uxwithjessicaengstrom/&quot;&gt;recent podcast episode about UX&lt;/a&gt;, I mentioned about my Aphantasia, and coincidentally enough, the guest said that she also had it too. And also a listener, who I also know through &lt;a href=&quot;https://www.dotnetoxford.com/&quot;&gt;.NET Oxford&lt;/a&gt;, Tweeted...&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Listening to &lt;a href=&quot;https://twitter.com/dracan?ref_src=twsrc%5Etfw&quot;&gt;@dracan&lt;/a&gt; podcast I found out I have Aphantasia. Had no idea this was a thing.&lt;/p&gt;&amp;mdash; Tom (@tomduttonuk) &lt;a href=&quot;https://twitter.com/tomduttonuk/status/1477390535702818824?ref_src=twsrc%5Etfw&quot;&gt;January 1, 2022&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;Since then, I&#39;ve chatted quite a bit with Tom about this. We&#39;ve both wondered if developers without this use their visualisation to help them with coding. If you&#39;re a non-Aphant programming reading this - I&#39;d love to hear your experiences about this.&lt;/p&gt;
&lt;h1&gt;Do you have Aphantasia?&lt;/h1&gt;
&lt;p&gt;If you read the next sentence, but don&#39;t read any further until you&#39;ve tried what it says...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Close your eyes, and imagine a ball on a table. Imagine pushing the ball off the table, and it falling onto the floor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&#39;m assuming you&#39;ve now opened your eyes again, otherwise you wouldn&#39;t be able to continue reading this! :) What colour was the ball? What colour was the table? Describe both. From what I&#39;ve read, most people with Alphantasia would probably say that it didn&#39;t have a colour (that would be my answer). For me, whilst I can imagine it - I can&#39;t actually &lt;em&gt;see&lt;/em&gt; anything, and I didn&#39;t even concider that it would have a colour.&lt;/p&gt;
&lt;p&gt;Another common test is to just try and imagine a red star in your mind&#39;s eye. Which of these is closest to what you see?...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2022-01-Aphantasia/RedStarTest.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(source: https://www.reddit.com/r/Aphantasia/comments/aioyga/simple_aphantasia_test/)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For me - it&#39;s #1.&lt;/p&gt;
&lt;p&gt;And here&#39;s another one...&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Think of a horse 🐴&lt;br /&gt;&lt;br /&gt;No, really. Close your eyes and think of a &lt;a href=&quot;https://twitter.com/hashtag/horse?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#horse&lt;/a&gt;. Which image best describes your &lt;a href=&quot;https://twitter.com/hashtag/experience?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#experience&lt;/a&gt;?&lt;a href=&quot;https://twitter.com/hashtag/Imagine?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Imagine&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/NoHorse?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#NoHorse&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/Aphantasia?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Aphantasia&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AphantasiaTest?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#AphantasiaTest&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/Extreme?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Extreme&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/Imagination?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#Imagination&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/BlindMind?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#BlindMind&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/AphantasiaLife?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#AphantasiaLife&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/MindsEye?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#MindsEye&lt;/a&gt; &lt;a href=&quot;https://t.co/iNokhjMY9A&quot;&gt;pic.twitter.com/iNokhjMY9A&lt;/a&gt;&lt;/p&gt;&amp;mdash; Aphantasia Network (@_aphantasia) &lt;a href=&quot;https://twitter.com/_aphantasia/status/1213995103607541766?ref_src=twsrc%5Etfw&quot;&gt;January 6, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;I&#39;ve always thought terms like &lt;em&gt;&amp;quot;picture X in your minds eye&amp;quot;&lt;/em&gt; were expressions, and not actually meant literally. When people meditate and say &lt;em&gt;&amp;quot;imagine yourself on a beach&amp;quot;&lt;/em&gt; - I had no idea people actually saw the beach. When people say to count sheep to try to fall asleep - I had no idea that people actually &lt;em&gt;saw&lt;/em&gt; sheep. In those memory-training books, where it describes having &amp;quot;mind palaces&amp;quot; having a scene in your mind where you mentally place things to help you remember them. Again - I had no idea people actually &lt;em&gt;saw&lt;/em&gt; their mind palaces!&lt;/p&gt;
&lt;h1&gt;How do I imagine things then?&lt;/h1&gt;
&lt;p&gt;Let&#39;s pretend the word &#39;imagine&#39; doesn&#39;t derive from &#39;image&#39;! If I &lt;em&gt;imagine&lt;/em&gt; that I&#39;m, for example, at my parents&#39; house - I still have a &lt;em&gt;mental model&lt;/em&gt; of the scene. I have a position in the scene, and walls/objects also have 3-D positions in that mental model. I just don&#39;t see anything. No colours. No edges. Nothing. Chatting with my dad, he doesn&#39;t understand how I can imagine scenes, without being able to see them. It&#39;s at this point that I realise it&#39;s very hard to describe what I experience. The closest word I can find to describe it is that it&#39;s a &lt;em&gt;feeling&lt;/em&gt; of the scene. Albeit, a very spacial feeling. It&#39;s a conceptual mental model.&lt;/p&gt;
&lt;h1&gt;Is this a disability?&lt;/h1&gt;
&lt;p&gt;When I first found out about this - I felt like I had just discovered I had a brain disability and my brain wasn&#39;t functioning properly. Everyone else has this super-power that I hadn&#39;t even known they&#39;d had.&lt;/p&gt;
&lt;p&gt;However, the more I read about it, and the more I speak to other people about what they experience - the more I realise that this isn&#39;t the case. Everyone&#39;s brains work very differently (much more so than I had ever thought). Speaking with my dad, it sounds like his thoughts are very dependent on his being able to see stuff in his mind&#39;s eye - which feels very limiting to me. Speaking with my brother - who is on the oppositive end of the spectrum having Hyperphantasis (see below), he says he hates it and wishes he was like me. I&#39;ve never understood meditation, and never needed to. Perhaps having a quieter mind is the reason why.&lt;/p&gt;
&lt;p&gt;In a Twitter DM conversation with Tom (see Tweet above), he said...&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;quot;Sounds like Sydney uni are doing lots of research into it. One professor said he thinks  he could make someone visualise with stimulation and training but comes with the cost that you couldn&#39;t undo it once you acquire visualisation. I wouldn&#39;t want to change the more I think about it.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think I agree. I suspect that the difference is much more than just seeing vs not seeing. And it&#39;s more that the way different people &lt;em&gt;think&lt;/em&gt; is fundamentially very different. I&#39;m not sure I&#39;d want to mess with that! But, I&#39;d love it if there was a magically way to experience it, but revert if I didn&#39;t like it.&lt;/p&gt;
&lt;h1&gt;Hyperphantasia&lt;/h1&gt;
&lt;p&gt;I mentioned above that my brother is on the opposite end of the spectrum. And it sounds like my eldest son also has this. Basically people with Hyperphantasia have extremely vivid mental imagery. Apparently this is more common than Aphantasia, and has been described as &lt;em&gt;as vivid as real seeing&lt;/em&gt;. (&lt;a href=&quot;https://en.wikipedia.org/wiki/Hyperphantasia&quot;&gt;wikipedia&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;So, this is basically a spectrum. From people with Aphantasia, who can&#39;t see anything - to people with Hyperphantasia, who have extreme imagery. And lots of people somewhere in the middle.&lt;/p&gt;
&lt;h1&gt;Other senses&lt;/h1&gt;
&lt;p&gt;Whilst Aphantasia/Hyperphantasia refers to the minds eye. It turns out that other people can also experience sound, smell, taste in their mind too. Sadly, I don&#39;t have any of these. I can sort-of hear my own inner voice (although, I wouldn&#39;t describe it has hearing exactly, but it&#39;s certainly using the spoken language). If I have a song stuck in my head, it&#39;s my own inner-voice humming it. My mum says that if she thinks of a song, she actually hears the artist&#39;s voice singing it. A friend, who&#39;s a fellow guitarist, who also has Aphantasia, says he can hear music quite vividly.&lt;/p&gt;
&lt;p&gt;I wonder how people sleep at night with all these sights and sounds going on in their mind!&lt;/p&gt;
&lt;h1&gt;I&#39;d love to hear your experiences!&lt;/h1&gt;
&lt;p&gt;I find this topic of conversation fascinating - and since finding out about this, I&#39;ve quizzed lots of different people about their experiences. Regardless of where on the spectrum you are, I&#39;d love to hear how you experience your thought!&lt;/p&gt;
&lt;h1&gt;Useful Links and Resources&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://aphantasia.com/&quot;&gt;Aphantasia Network&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/Aphantasia/&quot;&gt;Aphantasia sub-reddit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Year in Review: 2021</title>
    <link href="https://danclarke.com/2021-in-review/"/>
    <updated>2021-12-31T00:00:00Z</updated>
    <id>https://danclarke.com/2021-in-review/</id>
    <content type="html">&lt;p&gt;It&#39;s New Year&#39;s Eve, and I&#39;ve just remembered that I haven&#39;t done a &lt;em&gt;&#39;year in review&#39;&lt;/em&gt; blog post this year - so I&#39;m quickly knocking this out now! In fact, I&#39;m sorry to say, I haven&#39;t blogged much at all this year - with my primary focus being on the podcast. This will only be my second blog post of the year! Hopefully, the podcast content makes up for the lack of blog content! So let&#39;s start with that...&lt;/p&gt;
&lt;h1&gt;The Unhandled Exception Podcast&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-YearInReview/Mic.png&quot; alt=&quot;&quot; style=&quot;float:left; margin-right: 30px&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I started &lt;a href=&quot;https://unhandledexceptionpodcast.com/&quot;&gt;The Unhandled Exception Podcast&lt;/a&gt; near the end of last year, and spoke about it a bit in my &lt;a href=&quot;https://danclarke.com/2020-in-review&quot;&gt;last yearly update&lt;/a&gt;, and also in &lt;a href=&quot;https://danclarke.com/unhandled-exception-podcast&quot;&gt;a dedicated post about starting it&lt;/a&gt;. I&#39;m now up to 29 episodes, and have thoroughly enjoyed chatting with plenty of awesome guests about various software development topics. I&#39;ve also learnt a ton about podcast editing, which I also enjoy - although, it&#39;s certainly the most time-consuming part of podcasting! I took a bit of a break in December so I could catch up on other stuff - but will definitely be back in the new year with more episodes!&lt;/p&gt;
&lt;h1 style=&quot;clear:left&quot;&gt;.NET Oxford&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-YearInReview/DNO.png&quot; alt=&quot;&quot; style=&quot;float:left; margin-right: 30px&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.dotnetoxford.com/&quot;&gt;.NET Oxford&lt;/a&gt; is also still going strong. Still virtual for the time being, but that&#39;s given us the advantage of being able to welcome members from all over the planet!&lt;/p&gt;
&lt;p&gt;We will at some point return to physical events - perhaps even this coming year. But we&#39;ll have to ensure that however we do it - we retain some form of virtual presense, so that we don&#39;t exclude our new members that are too remote to physically attend. This won&#39;t happen in the first quarter of 2022 at least though.&lt;/p&gt;
&lt;h1 style=&quot;clear:left&quot;&gt;Everstack&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-YearInReview/EverstackLogo.jpg&quot; alt=&quot;&quot; style=&quot;float:left; margin-right: 30px&quot; /&gt;&lt;/p&gt;
&lt;p&gt;With my company, &lt;a href=&quot;https://www.everstack.com/&quot;&gt;Everstack&lt;/a&gt; - I&#39;ve started working with another big client - where I&#39;ve been really enjoying helping the team start to migrate their monolith codebase over to a new micro-service architecture running in the latest .NET, Azure, and Kubernetes. This has also involved a lot of mentoring, which I also really enjoy.&lt;/p&gt;
&lt;p&gt;On top of that, I&#39;ve also continued working with another of my main clients, where I&#39;ve been building the new version of their original software (which I also wrote many years ago), but instead of it being a desktop app - it&#39;s now web-based, multi-tenant, and can support their clients. This is also built on .NET, Azure, and Kubernetes.&lt;/p&gt;
&lt;p&gt;Since the pandemic started, I haven&#39;t been back to a clients&#39; office to work - with just two in-person meetings in total. What the pandemic has done to our industry regarding remote work, is perhaps one of the few positives that have come out of it. I&#39;m certainly very happy working remotely, and have no intention to go back to an office that isn&#39;t my home office. But given I&#39;d like to expand Everstack and grow in the future - the fact that remote is now the new norm, makes this a lot easier too. I&#39;d always expected to have to get physical premises (which are obviously very expensive, and limited to a &lt;em&gt;local&lt;/em&gt; team). Now I&#39;ve experienced working with remote teams, and can see that it works well - the barrier to entry for me expanding Everstack has now been dramatically lowered, and Everstack will most certainly be a remote company when it expands.&lt;/p&gt;
&lt;h1 style=&quot;clear:left&quot;&gt;Family life&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-YearInReview/Home.png&quot; alt=&quot;&quot; style=&quot;float:left; margin-right: 30px&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Nothing particularly new to report on the family front - both boys are still getting bigger by the day. We&#39;ve been very lucky with how well they get on with each other - they&#39;re best friends, which is perhaps rare for brothers of their age. My wife&#39;s also still working part-time remotely, which has worked really well.&lt;/p&gt;
&lt;p&gt;I&#39;ve also started regularly playing the guitar again. I used to play, but haven&#39;t picked it up much. I&#39;ve now started making sure I play/practice daily, and (touch wood), I seem to be sticking at it this time!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Snapshot Testing with Verify</title>
    <link href="https://danclarke.com/snapshot-testing-with-verify/"/>
    <updated>2021-12-10T00:00:00Z</updated>
    <id>https://danclarke.com/snapshot-testing-with-verify/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;This post is part of two awesome &lt;em&gt;advent calendar&lt;/em&gt; initiatives - the &lt;a href=&quot;https://dotnet.christmas/2021&quot;&gt;.NET Advent Calendar 2021&lt;/a&gt;, and the &lt;a href=&quot;https://www.csadvent.christmas/&quot;&gt;C# Advent Calendar 2021&lt;/a&gt;. Check out both links for awesome community blog posts!&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;In &lt;a href=&quot;https://unhandledexceptionpodcast.com/posts/0029-snapshottesting/&quot;&gt;a recent episode of my podcast&lt;/a&gt; (The Unhandled Exception podcast), I was joined by &lt;a href=&quot;https://twitter.com/SimonCropp&quot;&gt;Simon Cropp&lt;/a&gt; to chat about a form of testing called &lt;em&gt;snapshot testing&lt;/em&gt; - which has &lt;em&gt;completely&lt;/em&gt; changed the way I now write my tests!&lt;/p&gt;
&lt;div id=&quot;buzzsprout-player-9616767&quot;&gt;&lt;/div&gt;&lt;script src=&quot;https://www.buzzsprout.com/978640/9616767-snapshot-testing-with-simon-cropp.js?container_id=buzzsprout-player-9616767&amp;player=small&quot; type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;em&gt;Simon is the author of the &lt;a href=&quot;https://github.com/VerifyTests/Verify&quot;&gt;Verify&lt;/a&gt; .NET snapshot testing library, and also a maintainer of &lt;a href=&quot;https://github.com/approvals/ApprovalTests.Net/&quot;&gt;ApprovalTests&lt;/a&gt;, which is also a snapshot testing library.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&#39;d heard of snapshot testing before, but hadn&#39;t really used it. In the week leading up to recording the podcast, I started to play around with it, and have quickly become an avid fan. This is going to save me so much time writing tests, and the more I use it, the more I wonder why this isn&#39;t more of a commonly used testing methodology. Most developers I ask, haven&#39;t even heard of snapshot testing. I think that&#39;s a big mistake, given both how powerful it is, and how much time it can save.&lt;/p&gt;
&lt;h1&gt;What is snapshot testing?&lt;/h1&gt;
&lt;p&gt;Simon said near the start of the episode that perhaps the term &amp;quot;snapshot testing&amp;quot; is badly named, and it should instead be called &amp;quot;snapshot assertion&amp;quot;. This is because it&#39;s the &lt;em&gt;assert&lt;/em&gt; part of your test that this relates to.&lt;/p&gt;
&lt;p&gt;A simple example...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Arrange&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;MySystemUnderTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Verifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s only the last line that relates to snapshot testing. This will serialise &lt;code&gt;result&lt;/code&gt; in some form or another (depending on what it is), and write it to disk. This is known as the &amp;quot;received&amp;quot; file - ie. that&#39;s the output from the current test execution&#39;s verify. This gets compared to another file on disk called the &amp;quot;verified&amp;quot; file. If they&#39;re different, then your test fails. If they&#39;re the same, then your test passes.&lt;/p&gt;
&lt;p&gt;If you&#39;re paying attention, you might now be asking what creates that &amp;quot;verified&amp;quot; file in the first place. How does my test &lt;em&gt;ever&lt;/em&gt; pass? The answer is - the first time you run it - it will always fail. The act of copying the &amp;quot;received&amp;quot; file as the &amp;quot;verified&amp;quot; file is your way of saying &lt;em&gt;&amp;quot;I approve this version&amp;quot;&lt;/em&gt;, and this verified version goes into your source control system, alongside your changes. Note that the act of &lt;em&gt;copying&lt;/em&gt; the file, sounds very manual - but I haven&#39;t yet once had to &lt;em&gt;manually&lt;/em&gt; copy that file across, as my diff tool does this for me...&lt;/p&gt;
&lt;h1&gt;Diff-tool integration&lt;/h1&gt;
&lt;p&gt;How many times have you had a failing test, where the assertion is comparing JSON, and you&#39;ve had trouble working out what is different based on badly formatted JSON in the test output window? I quite often end up &lt;em&gt;manually&lt;/em&gt; copying and pasting the JSON into vscode so I can format the JSON - then copying both sides into WinMerge to compare.&lt;/p&gt;
&lt;p&gt;With Verify, not only do I not have to generate the &lt;em&gt;expected data&lt;/em&gt; in the first place (more on that shortly) - I automatically have my diff tool of choice, popup as soon as the test fails, showing me exactly what has changed!&lt;/p&gt;
&lt;p&gt;If I&#39;m happy with those changes, I just use my diff tool&#39;s UI or keyboard shortcut to copy those changes from left to right, and that&#39;s it! The saved right-hand side becomes the new &#39;truth&#39;, which then gets committed to source control alongside my main code changes.&lt;/p&gt;
&lt;p&gt;Note that my example above is talking about .NET objects being serialised to JSON. But Verify also works with tons of other content types (eg. images, PDFs, etc) - and it also integrates into tons of other libraries (eg. Playwright, Entity Framework, Blazor, ImageSharp, etc).&lt;/p&gt;
&lt;p&gt;Multiple diff tools are supported - and if you have more than one installed, you can configure which ones it should use with the &lt;a href=&quot;https://github.com/VerifyTests/DiffEngine/blob/main/docs/diff-tool.order.md&quot;&gt;DiffEngine_ToolOrder&lt;/a&gt; environment variable.&lt;/p&gt;
&lt;h1&gt;How much time does this save the developer?&lt;/h1&gt;
&lt;p&gt;Okay, before we go on - let&#39;s stop for a minute and contemplate how much time this actually saves...&lt;/p&gt;
&lt;p&gt;A lot of the tests I write are asserting against non-native objects - eg. DTOs or a collection of DTOs. Sometimes they might be nested. So without snapshot testing - I need to manually write code to generate the &lt;em&gt;expected data&lt;/em&gt; to assert against. For example...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Arrange&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; expectedData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;List&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Order&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        PriceNet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        PriceGross &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;120m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        OrderItems &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;List&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;OrderItems&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;OrderItem&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                Sku &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test-sku-1&quot;&lt;/span&gt;
                &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;etc&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;OrderItem&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                Sku &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test-sku-2&quot;&lt;/span&gt;
                &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;etc&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;etc&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;etc&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// yawn! it is hometime yet?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;MySystemUnderTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomeThing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;
result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;BeEquivalentTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&#39;s compare this to Verify...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Arrange&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;MySystemUnderTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomeThing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Verifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Need I say more? ;)&lt;/p&gt;
&lt;p&gt;When you run that Verify test for the first time - it&#39;ll pop up your diff tool...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Windiff.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I can review it to make sure it&#39;s what I expect. Then I can just use the diff tool&#39;s native way of copying from left to right. In WinMerge&#39;s case, it&#39;s this button...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Windiff-LeftToRightButton.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Save the file, and that&#39;s it! The test will now pass! You&#39;ll also have a new file, in this case, called &lt;code&gt;DemoTests.VerifyDemoTest.verified.txt&lt;/code&gt;, that can (and should!) be source controlled alongside your code changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Rider.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A few weeks roll by... and a bug gets introduced in some tax calculation code, which causes the test to fail. When running the failing test locally, it&#39;ll automatically pop up your diff tool...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Windiff-FailedTest.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That&#39;s so much more clear than messy unformatted JSON in the output window. In the output window - the two sides of the comparison aren&#39;t even side by side!&lt;/p&gt;
&lt;h1&gt;Guids and Timestamps&lt;/h1&gt;
&lt;p&gt;Sometimes in your test data, you have guids and timestamps that will be different each time your code is run. Verify automatically detects this, and replaces them with &lt;code&gt;DateTime_1&lt;/code&gt;..&lt;code&gt;DateTime_n&lt;/code&gt; for timestamps, and &lt;code&gt;Guid_1&lt;/code&gt;..&lt;code&gt;Guid_n&lt;/code&gt; for guids. Where the same GUIDs have the same &lt;code&gt;n&lt;/code&gt; value.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  Id: Guid_1,
  OtherId: Guid_2
  SomeValueThatIsTheSameGuidAsTheFirstId: Guid_1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are called &lt;em&gt;scrubbers&lt;/em&gt;, and you can also write your own too.&lt;/p&gt;
&lt;h1&gt;DiffEngineTray Tool&lt;/h1&gt;
&lt;p&gt;Simon insisted that I really need to be using the &lt;a href=&quot;https://github.com/VerifyTests/DiffEngine/blob/main/docs/tray.md&quot;&gt;DiffEngineTray&lt;/a&gt; tool. This is basically a system tray tool (Window only at the moment) that aids you when approving changes - also allowing you to accept in bulk...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/DiffEngineTray.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(screenshot taken from the project&#39;s readme)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can install it just by using the dotnet cli...&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;dotnet tool &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; DiffEngineTray&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I must admit, when I was just using Snapshot testing for .NET Objects that get serialised to JSON (as I described above) - I didn&#39;t see much use for this. I was quite happy accepting changes in WinMerge. &lt;em&gt;But&lt;/em&gt;... playing around a bit further, I&#39;m soon discovering that this is very useful when dealing with other types of files - eg. images or screenshots. Let&#39;s delve into an example of this using the &lt;a href=&quot;https://playwright.dev/&quot;&gt;Playwright&lt;/a&gt; UI testing tool and Verify Playwright extension...&lt;/p&gt;
&lt;h1&gt;Playing with Playwright&lt;/h1&gt;
&lt;p&gt;A simple example that just browses to the podcast website, and &lt;em&gt;verifies&lt;/em&gt; the results...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Initialisation&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ModuleInitializer&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; VerifyPlaywright&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Enable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PlaywrightTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; playwright &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Playwright&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; playwright&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Chromium&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LaunchAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewPageAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GotoAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://unhandledexceptionpodcast.com/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Verifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first run of that test, will pop up &lt;em&gt;two&lt;/em&gt; instances of my difftool. One with a screenshot...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/PlaywrightBeyondCompare.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And another with the HTML...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Winmerge-uep.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that because this is the first run - both the screenshot comparison and the HTML diff are empty on the right.&lt;/p&gt;
&lt;p&gt;The point here is that it opened multiple types of diff from a single Verify command against the Playwright page - one for the screenshot, and another for the HTML itself. I can now just right-click on the system tray tool, and choose &#39;Accept all&#39;...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Accept.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;...and life is green :) ...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/Playwright_Green.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Perhaps stop and take a moment to think about how &lt;em&gt;little&lt;/em&gt; code is in that Playwright test above - and how much value both Playwright and Verify gave us out of the box. It&#39;s very impressive.&lt;/p&gt;
&lt;h1&gt;Recording&lt;/h1&gt;
&lt;p&gt;Before wrapping up this post, I just want to cover another pretty awesome thing that Simon discussed in the podcast episode. Whilst the concept of &amp;quot;recording&amp;quot; isn&#39;t &lt;em&gt;just&lt;/em&gt; limited to Entity Framework and its generated SQL statements - this is something that piqued my interest, so I thought I&#39;d have a play...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;EfTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Arrange&lt;/span&gt;

    VerifyEntityFramework&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Enable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; builder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;DbContextOptionsBuilder&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;DataContext&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseSqlServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ConnectionString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;EnableRecording&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; db &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;DataContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;

    EfRecording&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StartRecording&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MyTables&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;MyTable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Guid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewGuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SaveChangesAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Verifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which on the first run, gives me this...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2021-12-SnapshotTesting/WinMerge-EF.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That&#39;s a &lt;em&gt;super&lt;/em&gt;-simple example - but you can see how easy it is to get Verify to generate a snapshot of the SQL that Entity Framework outputs. And other than calling &lt;code&gt;.EnableRecording()&lt;/code&gt; on the options builder that gets passed into your DataContext, no changes to your code are required. And bear in mind you&#39;d normally pass this in from your tests anyway - even in an integration test.&lt;/p&gt;
&lt;p&gt;A lot of the tests I write are integration tests where I spin up a &lt;em&gt;real&lt;/em&gt; database in Docker, and write tests against my APIs themselves (not each individual C# class). Verify is great because I can quickly verify the HTTP response and any data my tests query from the database to ensure the SUT did what was expected. However, thanks to this &lt;em&gt;recording&lt;/em&gt; concept - for free with just a couple of lines of code in the test - there&#39;s now the added benefit that I can also snapshot any SQL statements that my Entity Framework code has made, so every time it changes, tests fails, and I can review why it&#39;s different. Bear in mind that it might not &lt;em&gt;just&lt;/em&gt; change because my code has changed... it might change when updating to a new version of Entity Framework. Good to know if a new version of EF has changed your SQL (for better or for worse)!&lt;/p&gt;
&lt;h1&gt;A swath of extensions!&lt;/h1&gt;
&lt;p&gt;I&#39;ve touched on a couple of extensions in this blog post - but there are &lt;a href=&quot;https://github.com/VerifyTests/Verify#extensions&quot;&gt;a ton more&lt;/a&gt;. From Blazor verifiers, to ImageSharp! And from speaking to Simon, he&#39;s very keen for people to get in touch with use-cases for different extensions. He spoke in the podcast episode about how it was important to him that Verify was composable and extendible from the start. And that&#39;s clear in the list of extensions he has.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;The more I look into this and use it - the more I honestly don&#39;t understand how this isn&#39;t a more widely used way of asserting in tests. It saves a massive amount of time, makes diagnosing failing tests far easier, and fulfils multiple different use-cases and scenarios. It&#39;s certainly a tool that has been permanently added to my toolbox moving forward, and I&#39;ll now be introducing it to all teams that I consult for.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/VerifyTests/Verify&quot;&gt;Verify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/VerifyTests/Verify#extensions&quot;&gt;Verify Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/VerifyTests/DiffEngine/blob/main/docs/tray.md&quot;&gt;DiffEngineTray&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Year in Review: 2020</title>
    <link href="https://danclarke.com/2020-in-review/"/>
    <updated>2020-12-23T00:00:00Z</updated>
    <id>https://danclarke.com/2020-in-review/</id>
    <content type="html">&lt;p&gt;This year has &lt;em&gt;obviously&lt;/em&gt; been a lot different than everyone expected. I feel extremely fortunately and lucky to be in an industry that naturally makes it easy to work from home. In fact, remote work is something I&#39;ve wanted to try full-time for quite some time, and this year has not only forced it to happen, but also made it more normal in our industry (hopefully now moving forwards). It&#39;s just a shame it took something so terrible as what&#39;s happening for this to become a thing.&lt;/p&gt;
&lt;h1&gt;Azure Oxford on hold for the time being&lt;/h1&gt;
&lt;p&gt;It feels like a life-time ago thinking back to pre-March this year, but I guess that&#39;s still part of 2020, ergo this post. I mentioned in &lt;a href=&quot;https://danclarke.com/2019-in-review&quot;&gt;last year&#39;s update&lt;/a&gt; that I&#39;d started &lt;a href=&quot;https://www.azureoxford.com/&quot;&gt;Azure Oxford&lt;/a&gt; together with James World. On-top of some great events in 2019, we also had two in 2020 - January and February before the lockdown hit. We happened to be a bit behind planning our March event, when news of the Coronavirus hit, so we decided to wait to see what happened. Sadly, we all know what happened, and we then decided to put Azure Oxford on hold. I don&#39;t want to say Azure Oxford has been stopped for good, but James and I haven&#39;t as of yet discussed bringing it back. A large part of it not coming back virtually (like we did for .NET Oxford) has been time. With the kids being at home for the lockdown (so lunch breaks where I normally got things done were spent with them) and my new contract (see below) being an 8-hour day rather than previous 7.5 (so 2.5 hours more each week) - I just didn&#39;t have time to run two user-groups.&lt;/p&gt;
&lt;h1&gt;.NET Oxford goes virtual&lt;/h1&gt;
&lt;p&gt;As for &lt;a href=&quot;https://www.dotnetoxford.com/&quot;&gt;.NET Oxford&lt;/a&gt;, because this has been going for quite a few years and has quite a decent community around - we decided to attempt to keep this going virtually. And this actually worked &lt;em&gt;really&lt;/em&gt; well! Thanks to technologies like Zoom, this has just worked. In-fact, far more people are now able to attend than they could have done in person. Obviously it&#39;s not quite the same as an in-person event - but it&#39;s still working surprisingly well. We still also get the regulars attending the &lt;em&gt;virtual&lt;/em&gt; pub session afterwards (we used to meet in the physical pub after in-person meetups). It&#39;s obviously bring your own drinks - but it works! Being virtual, we&#39;ve also had the opportunity to have speakers where there&#39;s no way we could have gotten otherwise - for example, in June we had &lt;a href=&quot;https://www.dotnetoxford.com/posts/2020-06-scott-hunter&quot;&gt;Scott Hunter join us&lt;/a&gt;, and in September we were joined &lt;a href=&quot;https://www.dotnetoxford.com/posts/2020-09-wsl-with-scott-hanselman&quot;&gt;by Scott Hanselman&lt;/a&gt;! There is a down-side though with famous speakers being able to talk at user-groups all over the world from their own homes... it&#39;s become harder for newer speakers to start getting into public speaking. We&#39;ve had a couple of lightning-talk events where some of the speakers have been first-time speakers, but I&#39;m quite keen in the new year for our next lightning-talk event to be &lt;em&gt;primarily&lt;/em&gt; for new speakers to help encourage developers who fancy giving speaking a go, so dip their toes in!&lt;/p&gt;
&lt;h1&gt;Starting my own podcast!&lt;/h1&gt;
&lt;p&gt;I&#39;ve been an avid podcast listener for many years, and have even vaguely wondered what it might be like to start my own. But I never would have actually done it if it hadn&#39;t have been for all the other changes that this year has brought. When I started working remotely full-time, knowing that I&#39;d also be hosting .NET Oxford virtually - I decided to invest in a decent microphone. A new contract (see below) had me pair-programming remotely all day, so I quickly got very used to talking &#39;programming&#39; all day into the mic. When I first invested in the mic near the start of the lockdown, giving podcasting a go was also in the back of my mind - but due to the kids being at home all the time, I just didn&#39;t have the time. Then in October, I started &lt;a href=&quot;https://unhandledexceptionpodcast.com/&quot;&gt;The Unhandled Exception&lt;/a&gt; podcast, and ended the year eight episodes in! I&#39;ve had great feedback, and at the time of writing this, have had 1,134 downloads! I&#39;ve really enjoyed all aspects of it - from planning the episodes, to chatting and geeking out with the guests, and have also really enjoyed editing and learning all about audio!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.danclarke.com/unhandled-exception-podcast&quot;&gt;Blog post: Starting &#39;The Unhandled Exception&#39; podcast&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Sad news: Sharp Life Science becomes a victim of the pandemic&lt;/h1&gt;
&lt;p&gt;In &lt;a href=&quot;https://danclarke.com/2019-in-review&quot;&gt;last year&#39;s update&lt;/a&gt;, I spoke about my company Everstack&#39;s primary contract/client at the time - &lt;a href=&quot;https://www.aqdrop.com/&quot;&gt;Sharp Life Science&lt;/a&gt;. This was a medical company building hardware and software to help automate genomic and proteomic applications. It was a fantastic company and team of people to work with, some of those people I&#39;m now fortunate enough to consider good friends. In March, naturally more and more staff moved to remote working, which actually suited me quite well - but then sadly, it became apparent that the parent company was planning to wind down the company. It was a startup that was investment backed - and the parent company had to focus on the pandemic.&lt;/p&gt;
&lt;h1&gt;New Contract&lt;/h1&gt;
&lt;p&gt;After finishing at SLS, I did a little bit of consulting for a couple of other companies before finding a longer term client. This is a 100% remote team anyway, even before the pandemic, and I&#39;ve had the opportunity to work with a lot of really cool technologies! .NET Core, Docker, Kubernetes, Azure, React, etc. They also do TDD, which is the first project I&#39;ve worked on that has been properly TDDed from the start. It&#39;s been quite eye-opening, and has certainly shown me the true value of doing TDD! It&#39;s also a really great team, that I really enjoy working with.&lt;/p&gt;
&lt;h1&gt;Remote working&lt;/h1&gt;
&lt;p&gt;I&#39;ve mentioned a couple of times above about remote working. And this is something most of us have had to get used to this year. It&#39;s actually something I&#39;ve wanted to try full-time for quite some time to see if I got on with it. And it turns out that I quite enjoy it, and I&#39;m quite keen to continue doing this, and not go back to an office. I love my home office, and have it set up with decent audio/video equipment, &lt;a href=&quot;https://danclarke.com/standing-desk-part1&quot;&gt;a standing desk&lt;/a&gt;... It&#39;s &lt;em&gt;home&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I miss the commute where I could listen to podcasts, but have replaced it instead with daily walks or runs where I listen to podcasts instead. It perhaps helps a lot though that &lt;a href=&quot;https://danclarke.com/2020-pair-programming&quot;&gt;I pair-program a lot&lt;/a&gt; with the team I&#39;m currently working with, so it&#39;s a lot less isolating. It&#39;s possible that in the future I might change my mind about remote work, if I&#39;m working with a team that&#39;s in the office, and I&#39;m the only person working remotely. Time will tell!&lt;/p&gt;
&lt;h1&gt;MVP renewed!&lt;/h1&gt;
&lt;p&gt;I&#39;m also very honoured to say that my Microsoft MVP status was renewed in July this year! I&#39;m very proud to be considered part of this amazing group of people! I was really looking forward to attending my first MVP Summit in Redmond in March, but sadly that wasn&#39;t to be. Maybe in a year or so I&#39;ll have the opportunity again.&lt;/p&gt;
&lt;h1&gt;Public speaking&lt;/h1&gt;
&lt;p&gt;I haven&#39;t really done that much public speaking this year. There&#39;s been the .NET Oxford intro talks, and I also did a couple of lightning talks. One of the JetBrains Rider IDE, and the other on the MediatR library. Details (inc videos) can be found on my &lt;a href=&quot;https://danclarke.com/public-speaking&quot;&gt;public speaking page&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Home and Family life&lt;/h1&gt;
&lt;p&gt;From a non-tech point of view, nothing much has changed really. The boys are continuing to get bigger every time I turn around! Mason, my youngest, started school (reception year), and Jack&#39;s now in year two. My wife&#39;s work has luckily been extremely flexible over the lockdown period, and she was able to take the brunt of childcare duties whilst I worked during the day. The downside of being a contractor is that if you don&#39;t work, your company doesn&#39;t get paid!&lt;/p&gt;
&lt;h1&gt;Taking a moment to think about those less fortunate&lt;/h1&gt;
&lt;p&gt;I feel &lt;em&gt;extremely&lt;/em&gt; lucky and privileged during this year that I have such an amazing family at home for company. If I think back to when I first moved down to Oxford and was living in a tiny house on my own, and imagine that it was &lt;em&gt;then&lt;/em&gt; that the pandemic hit - things would have so different. But even then, whilst I would have been very lonely and isolated, I&#39;d still have been in an industry where I most likely would have been in work. If you&#39;re in a similarly fortunate situation, it&#39;s important that we take a moment and really appreciate how lucky we are. I can&#39;t even imagine what this year has been like for those that have lost their jobs, can&#39;t afford to feed their families, are completely on their own, lost and depressed; or have lost loved ones. For a lot people in our industry, jumping on a Zoom call and using computers to communicate is effortless and makes the world a smaller place, whilst lessening the impact of the pandemic. Not everyone is computer literate, or wants to be. Most people need other people, and using a computer as a medium for that social interaction just isn&#39;t the same. This year has been pretty shit for so many people, and we can only hope that 2021 improves. Stay safe everyone.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Starting &#39;The Unhandled Exception&#39; podcast</title>
    <link href="https://danclarke.com/unhandled-exception-podcast/"/>
    <updated>2020-11-01T00:00:00Z</updated>
    <id>https://danclarke.com/unhandled-exception-podcast/</id>
    <content type="html">&lt;p&gt;I have some exciting news! As you may already know if you follow me on Twitter, I&#39;ve recently started my very own podcast!!&lt;/p&gt;
&lt;p&gt;TLDR; - If you&#39;re not interested in the &lt;em&gt;whys&lt;/em&gt; or &lt;em&gt;hows&lt;/em&gt;, and just want to listen, then head over to &lt;a href=&quot;https://unhandledexceptionpodcast.com/&quot;&gt;The Unhandled Exception Podcast website&lt;/a&gt;. This includes show notes, and also links to the major podcast apps, so you can listen using your favourite podcast app (and hopefully subscribe!).&lt;/p&gt;
&lt;p&gt;If you&#39;re interested in reading more about not just why I&#39;ve started it, but also my learning and experience so far, then read on...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-UnhandledExceptionPodcast/mics.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Why start a podcast?&lt;/h1&gt;
&lt;p&gt;I&#39;ve been an avid listener of podcasts for &lt;em&gt;many&lt;/em&gt; years, and I&#39;ve learnt a huge amount about various programming topics over time because of this. The great thing about podcasts, is that you can listen whilst doing other things - eg. your commute (pre-covid that is!); walks or runs; doing stuff around the house; mowing the lawn, etc. I find video content much more difficult to find time to consume.&lt;/p&gt;
&lt;p&gt;The benefit to listening to podcasts for me also hasn&#39;t just been about learning either. Hearing the &lt;em&gt;passion&lt;/em&gt; the hosts and guests have about both our industry and the wide range of technologies that we&#39;re lucky enough to have to play - is one of the reasons I&#39;ve grown to become so passionate about software development myself, and got into blogging, started .NET Oxford, and public speaking, and now this podcast.&lt;/p&gt;
&lt;p&gt;I love chatting with people about programming technologies, and have quite often found myself thinking, when at user-groups or conferences, that an indepth conversation I&#39;m in with one or more people would have made an awesome podcast episode if only it had been recorded! Through .NET Oxford and going to conferences, I&#39;ve met so many amazing people. I really enjoy the tech talks at these events, but quite often find that the conversations inbetween the talks, or afterward are just as valuable. These aren&#39;t a one-way &amp;quot;one-to-many&amp;quot; talks, and can quite often go in different directions. This is what I want to try and capture in this podcast.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The rest of this blog post will focus on my initial setup, what tools I&#39;m using, and some of the things I&#39;ve learnt so far...&lt;/p&gt;
&lt;h1&gt;Hosting&lt;/h1&gt;
&lt;p&gt;One of the first things I looked into was how to host the podcast. Being a developer, I can easily knock up a website and throw the mp3s onto something like Azure Storage. However, there are plenty of other things - eg. registering with all the major podcast services; generating stats; making sure the RSS feed is setup correctly, etc, etc. So I decided to go with one of the popular podcast hosting platforms, so all that stuff has been done for me, is tried and tested, so I can focus on the podcast.&lt;/p&gt;
&lt;p&gt;After a bit of research, I chose &lt;a href=&quot;https://www.buzzsprout.com/?referrer_id=922207&quot;&gt;BuzzSprout&lt;/a&gt;, which seemed to tick all the boxes, and was well spoken about in my research. I&#39;m quite happy with this choice so far. It&#39;s been very easy, and was effortless to add my podcast to all the major providers.&lt;/p&gt;
&lt;p&gt;Here&#39;s a screenshot of the management toolbar...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-UnhandledExceptionPodcast/buzzsprout-header.jpg&quot; alt=&quot;buzzsprout-header&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Episodes&lt;/em&gt; tab is obviously where you manage all your episodes.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Players&lt;/em&gt; tab gives you embeddable HTML that you can put on websites, etc. You can embed players for single episodes (like I do on the website, which I&#39;ll talk about shortly), or you can create players for multiple episodes.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Website&lt;/em&gt; tab allows you to create a website. The preview for this looks pretty much what you&#39;d expect. I decided to create my own site though. Not because there was anything wrong with the Buzzsprout generated site - but more because I had been looking for an excuse to play with &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt; and &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt;! It also gives me more scope to expand and improve the site how I want over time.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Directories&lt;/em&gt; tab gives you a list of the main podcast services (Apple Podcast, Spotify, Stitcher, etc), and allows you to click each to register. It was super-simple to register on each, and it just worked.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Monetization&lt;/em&gt; allows you to add sponsors. I haven&#39;t used this yet. Currently, the Podcast&#39;s current only sponsor is my own company, &lt;a href=&quot;https://www.everstack.com/&quot;&gt;Everstack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Stats&lt;/em&gt; tab gives you charts and numbers about downloads...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-UnhandledExceptionPodcast/buzzsprout-stats.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The first bump you see there is the day I first &lt;a href=&quot;https://twitter.com/dracan/status/1321453138537336839&quot;&gt;announced the podcast on Twitter&lt;/a&gt;, and the second is when I &lt;a href=&quot;https://www.linkedin.com/posts/danclarkeuk_the-unhandled-exception-podcast-activity-6727934272944001024-LLV7&quot;&gt;mentioned it on LinkedIn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You also get various other stats - breakdowns per episode, podcast app, locations, devices, etc.&lt;/p&gt;
&lt;h1&gt;Remote Interview platforms&lt;/h1&gt;
&lt;p&gt;With all that in place - how do we do remote interviews? I wanted to ensure that each persons&#39; audio was recorded to a separate channel (the importance of which was confirmed when I was editing the first guest episode!). Also, it&#39;s better to have audio recorded on each persons&#39; computer locally, rather than me recording everything on my end. You can do this manually and ask guests to record their audio themselves and send it to you. Or you can use a service to do this for you. Two popular ones are &lt;a href=&quot;https://squadcast.fm/&quot;&gt;Squadcast&lt;/a&gt; and &lt;a href=&quot;https://zencastr.com/&quot;&gt;ZenCastR&lt;/a&gt;. They record each guest locally in high quality wav format, then automatically upload it to your account. Both have high praise when reading about them online. I went with Squadcast because they also support video during the call. Recording the video isn&#39;t support, but it&#39;s still nice to be able to see the other person during the interview. ZenCastR does have a video in beta, but when I went to try it, I had to go on a waiting list. This was my deciding factor, as I wanted to use it straight away, and Squadcast worked really well during my first guest episode with Derek Comartin! So I&#39;m going to stick with this for the time being.&lt;/p&gt;
&lt;h1&gt;Microphones&lt;/h1&gt;
&lt;p&gt;At the start of lock down I invested in a &lt;a href=&quot;https://www.bluemic.com/en-us/products/yeti-x/&quot;&gt;Blue Yeti X&lt;/a&gt; microphone. This has served me really well, as I&#39;ve been remotely pair programming all day, so it gets used &lt;em&gt;all the time&lt;/em&gt;. It&#39;s also great when I&#39;m in meetings, as I can walk around the room and my voice still gets picked up almost as if I was right next to it. However, I&#39;m finding for podcasting, this is a big disadvantage. It literally picks up &lt;em&gt;everything&lt;/em&gt; - from room echo, to my kids shouting upstairs.&lt;/p&gt;
&lt;p&gt;My first couple of episode were recorded with the Blue Yeti, and whilst you couldn&#39;t call it an &lt;em&gt;echo&lt;/em&gt; as such - there&#39;s definitely an &#39;in a box&#39; kind of sound to it. I&#39;ve heard of people doing all sorts of things to get around this - eg. putting pillows and blankets all over their walls, even recording under a duvet! Doing a bit of reading on this, it turns out that there are two types of microphones - condenser and dynamic. The Blue Yeti is a condenser mic, which due to the way it works, picks up &lt;em&gt;everything&lt;/em&gt; (as I&#39;ve found out). For dynamic mics, you want to be much closer to the microphone and it picks up a lot less background noise. I don&#39;t regret getting the Blue Yeti, as it&#39;s certainly had &lt;em&gt;a lot&lt;/em&gt; of use over the past seven months, and it&#39;s probably a great mic for podcasting if you&#39;re in a treated quiet room with no background noise. But it&#39;s not really working for me and my home-office setup as far as podcast recordings are concerned. I wanted to try out a dynamic mic, and coincidentally I found a very old &lt;a href=&quot;https://www.amazon.co.uk/Behringer-XM8500-Ultravoice-Cardioid-Microphone/dp/B0002KZAKS&quot;&gt;Behringer XM8500&lt;/a&gt; budget mic in one of my cupboards (£15!). According to Amazon, I bought this in 2013. This would have been when I was playing the guitar a lot more and tried a bit of singing whilst playing (probably a good job I didn&#39;t continue with that!). Either way, this is a dynamic microphone (albeit quite a cheap one). It&#39;s XLR, meaning I also needed an audio interface or mixer - so last week I ordered a &lt;a href=&quot;https://www.amazon.co.uk/gp/product/B07QR73T66&quot;&gt;Focusrite Scarlett 2i2&lt;/a&gt; audio interface, and did a few test recordings with the XM8500. And I found that there&#39;s a &lt;em&gt;huge&lt;/em&gt; difference in lack of background noise compared to the Blue Yeti! Remember this is a £15 mic! Although, obviously there&#39;s the additional cost of the external audio interface, which the Blue Yeti doesn&#39;t require. Whilst the XM8500 sounds pretty good for a £15 mic - given I&#39;ve now invested in the audio interface, I&#39;ve decided to upgrade to the &lt;a href=&quot;http://www.rode.com/microphones/podcaster&quot;&gt;Rod Podcaster&lt;/a&gt;, which is also a dynamic mic, and should &lt;em&gt;hopefully&lt;/em&gt; sound better than the XM8500. I&#39;m looking forward to this arriving next week so that I can compare all three mics - the Rode, XM8500, and the Blue Yeti! Either way - the moral of the story is that if you&#39;re buying a mic for podcasting and you&#39;re not in a treated studio - do some research on dynamic vs condensor mics. It&#39;s too early for me to say I recommend a dynamic mic over a condenser mic - but from what I&#39;ve experienced so far (and my various readings), it definitely feels that way.&lt;/p&gt;
&lt;h1&gt;Audio Editing&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-UnhandledExceptionPodcast/audacity.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another part of all this is the audio editing. This is actually something I&#39;m quite enjoying. So far I&#39;ve been using &lt;a href=&quot;https://www.audacityteam.org/&quot;&gt;Audacity&lt;/a&gt;, which is a fantastic open-source cross-platform application. It&#39;s super-easy to modify multiple tracks, chop out bits, and move stuff around. I&#39;m finding it amazing how easy it is to tidy up audio without being able to tell when listening back. Eg. removing &lt;em&gt;erms&lt;/em&gt;, and &lt;em&gt;ums&lt;/em&gt;, pauses, etc. Also because each speaker&#39;s audio is recorded to a separate channel, it&#39;s easy to clean up cross-talk where both speakers start to talk at the same time, and make it sound seamless. Once used to using Audacity, especially if you learn a few of the keyboard shortcuts - it becomes very efficient to do this editing.&lt;/p&gt;
&lt;p&gt;I&#39;m also currently trialing &lt;a href=&quot;https://www.adobe.com/uk/products/audition.html&quot;&gt;Adobe Audition&lt;/a&gt; to see which I prefer. I&#39;ve quite often found that it would be useful to have some of Adobe&#39;s other apps - eg. Photoshop, Illustrator, etc. But couldn&#39;t really justify the ongoing subscription cost. However, having all of those, plus now having Adobe Audition for podcast editing - it&#39;s probably going to be enough to warrent the all-apps subscription.&lt;/p&gt;
&lt;h1&gt;The website&lt;/h1&gt;
&lt;p&gt;As mentioned above, I could have just used the automatically generated Buzzsprout website. But I decided to create my own to (a) give me greater flexibilty to grow and change it over time, but also (b) to have an excuse to play with the &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt; static site generator, and also &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions&lt;/a&gt;! The details of these are best saved for a different blog post, but I&#39;ve been very impressed with both! Hugo makes it very easy to create these sites (The UEP website source code can be found &lt;a href=&quot;https://github.com/dracan/UnhandledExceptionPodcastWebsite&quot;&gt;here&lt;/a&gt;), and GitHub actions build/deploy time is insanely quick! I normally use Azure Pipelines, but GHA seems to deploy so much faster. But that might be a Hugo thing too.&lt;/p&gt;
&lt;p&gt;The show notes will appear both on the website, and also in your podcast app. This will include all relevant links from each show.&lt;/p&gt;
&lt;p&gt;I also plan on adding support for comments to the website fairly soon.&lt;/p&gt;
&lt;p&gt;https://unhandledexceptionpodcast.com/&lt;/p&gt;
&lt;h1&gt;Guests&lt;/h1&gt;
&lt;p&gt;A huge part of podcasting is the guests. Whilst I&#39;ll do some episodes solo, I&#39;ll try to keep them short, as a big part of this is the conversational aspect - eg. it&#39;s not just someone &lt;em&gt;doing a talk&lt;/em&gt;. I have a list in my notes of people I&#39;d love to have on, including a couple I&#39;ve asked already and have agreed to join me for an episode.&lt;/p&gt;
&lt;p&gt;I was extremely lucky in that for my &lt;a href=&quot;https://unhandledexceptionpodcast.com/posts/0002-derekcomartin/&quot;&gt;first guest episode&lt;/a&gt;, Derek was extremely helpful and supportive, and made it super-easy. That was the first time I&#39;ve ever done something like that, and I quickly learnt an awful lot through doing it. Enough that I can hopefully at least sound like I know what I&#39;m doing next time! So a huge thank you to Derek for being such an awesome first guest!&lt;/p&gt;
&lt;h1&gt;Looking forwards&lt;/h1&gt;
&lt;p&gt;I hope this post has been useful if you&#39;ve been considering doing similar yourself. Or even if you&#39;ve not, but are interested in my initial experiences.&lt;/p&gt;
&lt;p&gt;This is early days yet, but I&#39;m really enjoying this so far. Feedback has been positive so far too. I have some great guests in the pipeline which I&#39;m really looking forward to recording episodes with.&lt;/p&gt;
&lt;p&gt;The more popular this becomes, the more content I&#39;ll be able to create - so if you can help me out by sharing on social media, and rating the podcast in your favourite podcast app, that would be amazing!&lt;/p&gt;
&lt;p&gt;Happy listening! :)&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Standing desk: Part 1 - Initial thoughts</title>
    <link href="https://danclarke.com/standing-desk-part1/"/>
    <updated>2020-10-04T00:00:00Z</updated>
    <id>https://danclarke.com/standing-desk-part1/</id>
    <content type="html">&lt;p&gt;As an industry, we spend &lt;em&gt;far&lt;/em&gt; too much time sat down. A quick Google search will show plenty of research suggesting that this is very bad for you in many different ways. I&#39;ve very recently invested in a standing desk, and thought I&#39;d share what led up to me buying it, which one I chose, and also my initial experiences using it. This will be a &#39;part 1&#39;, with a follow up in about 6 months.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-StandingDesk/office.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I work remotely, and a month or so ago I decided to start standing up during daily &lt;em&gt;standups&lt;/em&gt; and meetings. I was strongly aware that I had a very sedentary lifestyle - sitting down ~8-9 hours, five days a week. In my current contract, we pair &lt;a href=&quot;https://danclarke.com/2020-pair-programming&quot;&gt;pair-program remotely&lt;/a&gt; pretty much all day - so as well as standing up in meetings, I also tried standing up when the other person was &#39;driving&#39;.&lt;/p&gt;
&lt;p&gt;When doing this, I immediately found myself listening more intently during standups and meetings, and generally getting less distracted. I also found myself walking around the room and stretching whilst listening. Standing up didn&#39;t mean I was just going from a &lt;em&gt;stationary&lt;/em&gt; sit to just a &lt;em&gt;stationary&lt;/em&gt; stand. When stood up, I found myself naturally moving around &lt;em&gt;a lot&lt;/em&gt; more!&lt;/p&gt;
&lt;p&gt;The problem was that my monitors were still at sitting height. So I was looking down a lot - which isn&#39;t good for your upper spine. I also had to sit when I was typing. This is when I decided I was going to invest in a standing desk...&lt;/p&gt;
&lt;h1&gt;My desk choice&lt;/h1&gt;
&lt;h2&gt;The frame...&lt;/h2&gt;
&lt;p&gt;After a bit of research, and a few conversations on Twitter - I choose a &lt;a href=&quot;https://flexispot.co.uk/height-adjustable-desks/electric-standing-desk-frames-3-e7.html&quot;&gt;Flexispot E7&lt;/a&gt; desk. I knew I wanted a motorised desk that supported presets, so I could save my preferred heights. As a bonus, it also has a child-lock - which has turned out to be really useful, as my two kids think my new desk is a shiny new toy! Well, it is - but not &lt;em&gt;their&lt;/em&gt; toy! ;)&lt;/p&gt;
&lt;p&gt;It was a bit of a wait, taking just over a month to arrive once I ordered it. But once it arrived, it was very easy to assemble - I did it during my lunch break. I know someone who has ordered the same desk since, and theirs arrived much faster - so they must just have been out of stock when I ordered mine.&lt;/p&gt;
&lt;h2&gt;The desktop&lt;/h2&gt;
&lt;p&gt;The desktop is also from Flexispot, and I ordered it together with the frame...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-StandingDesk/desktop-edge.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This image is from their website (&lt;a href=&quot;https://flexispot.co.uk/height-adjustable-desks/desk-top-only/curved-desktop.html&quot;&gt;product page here&lt;/a&gt;). I got the black one. I find that that having this curved contoured edge makes a big difference. I guess this also applies to sitting down desks too - where using my old desk, I noticed my wrist resting against the square edge. Whereas with this desk, my wrist naturally rests and the contoured edge and feels much more comfortable.&lt;/p&gt;
&lt;h1&gt;Initial experience standing throughout the day&lt;/h1&gt;
&lt;h2&gt;Short-lived back pains (largely due to an old back injury)&lt;/h2&gt;
&lt;p&gt;My desk arrived on a Wednesday, and I used it on the Thursday and Friday. Over the weekend, my back was really sore. &lt;a href=&quot;https://twitter.com/dracan/status/1307224266325327873&quot;&gt;Speaking to people on Twitter&lt;/a&gt;, it sounds like I probably did too much too quickly, as I pretty much did most of those days stood up straight away. I also have an old lower-back injury that reoccurs from time to time, and causes me issues for a couple of days, then goes back to normal. Those two initial days of standing did trigger my bad back - but, luckily it only lasted a few days, then I found I could go back to standing - taking it a bit easier this time. It&#39;s only a couple of weeks later that I&#39;m writing this now, and I&#39;m already finding I can stand for most of the day and it&#39;s no longer an issue.&lt;/p&gt;
&lt;h2&gt;Moving around much more&lt;/h2&gt;
&lt;p&gt;As mentioned earlier in this post, I&#39;m finding that I&#39;m not just standing still, but moving around much more than I would have been when sat down. When listening in a meeting, I walk around my office whilst listening. I find myself stretching out more too. Over the course of months and years - the accumulative effect of this must be massive.&lt;/p&gt;
&lt;h2&gt;Stepping back from the screen&lt;/h2&gt;
&lt;p&gt;Another thing I find myself doing, is stepping back from the screen much more. If I&#39;m looking at code and thinking something through, but not typing - I&#39;m finding that I naturally step back to ponder. Much further back than I&#39;d be if I was sat down. So this isn&#39;t only good for your back, legs, cardiovascular system, etc -  but also your eyes too!&lt;/p&gt;
&lt;h2&gt;Bonus: Much easier to clean underneath too!&lt;/h2&gt;
&lt;p&gt;When I removed my old desk - it was pretty disgusting behind it. Getting the hoover under the new standing desk is much easier, as I can just raise it to full height and walk under it! It goes surprisingly high!&lt;/p&gt;
&lt;h1&gt;Accessories&lt;/h1&gt;
&lt;p&gt;There are a few different &lt;em&gt;accessories&lt;/em&gt; you can get as part of your move to standing. Below are the ones I went with...&lt;/p&gt;
&lt;h2&gt;Anti-fatigue mat&lt;/h2&gt;
&lt;p&gt;After speaking with people I know who have standing desks, and also &lt;a href=&quot;https://twitter.com/dracan/status/1306906865830424576&quot;&gt;asking on Twitter&lt;/a&gt;, I decided to get an anti-fatigue mat. The idea is that it&#39;s more cushioned and reduces pressure on your feet, legs, and back.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-StandingDesk/mat.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I must admit, that when I first got it, it didn&#39;t feel that much different to the carpet I have in my study anyway. However, using it more - I can definitely notice a difference. It just goes to show how a seemly small difference in floor, can make a big difference for prolonged periods of time!&lt;/p&gt;
&lt;p&gt;I went with the &lt;a href=&quot;https://www.amazon.co.uk/gp/product/B076ZDH199&quot;&gt;Flexispot anti-fatigue mat&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Wobble board&lt;/h2&gt;
&lt;p&gt;Whilst researching the anti-fatigue mats, I also came across something called a &lt;a href=&quot;https://www.amazon.co.uk/gp/product/B07HD838VP&quot;&gt;wobble board&lt;/a&gt;...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-StandingDesk/wobbleboard-both.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(photo on left taken from Amazon - those are *not* my socks ;))&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I ordered both the mat and the board at the same time, as I correctly suspected that being able to swap between different standing surfaces would make a difference when standing for long periods of time. When using this, I tend to have my feet much wider apart than in the image above, and do rocking motions. I&#39;m naturally quite fidgety anyway - and this board seems to work quite well for that. When on the mat or floor, I walk around more and pace - and when on the board, I find myself slightly bending each leg and rocking to and fro. It also feels very good for my lower back and knees, and the rocking motion really engages your core.&lt;/p&gt;
&lt;h2&gt;Wobble stool&lt;/h2&gt;
&lt;p&gt;The final accessory I got was a &lt;a href=&quot;https://flexispot.co.uk/wobble-stool-stay-active-exercise-office-chair-encourage-movement-height-adjustable-seat-for-comfortable-working-standing-desk-perching-stool.html&quot;&gt;wobble stool&lt;/a&gt;...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-10-StandingDesk/wobblestool.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Quite often I find that I want a break from standing just for 5 minutes. I don&#39;t want to move the desk all the way down to the low seating position and drag my chair in. I just want a short break perching for a bit. This is where a standing stool comes in. It takes up a lot less space than a full-on chair, and it can be adjusted in height, so you&#39;re still effectively &lt;em&gt;standing&lt;/em&gt;, but just taking the load off your back for a bit.&lt;/p&gt;
&lt;p&gt;This works well, and because it not only rocks from side to side, but also can swivel vertically along the y-axis - you get a different type of movement. So even when perched, I&#39;m finding I&#39;m not as stationary as when sitting in a normal office chair.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;I&#39;m &lt;em&gt;really&lt;/em&gt; pleased with this desk, and if I could go back in time, I wouldn&#39;t change any of my choices - this includes the frame, desktop, and the accessories.&lt;/p&gt;
&lt;p&gt;As I mentioned at the start - this is a &lt;em&gt;part 1&lt;/em&gt; post, as I&#39;ve only had the desk for a couple of weeks. I intend to write a follow-on post in about 6 months.&lt;/p&gt;
&lt;p&gt;Coincidentally, a few days after getting the desk, I happened to be having a health check, and discovered that my blood pressure was very high. This surprised me, as I eat healthily, don&#39;t smoke, only drink infrequently in moderation, exercise, have hardly any body fat, and never really get stressed. I&#39;m generally very happy in life, and feel very healthy. So really wasn&#39;t expecting to have high blood pressure. They do call it &lt;em&gt;the silent killer&lt;/em&gt; for this reason though! The only thing I can associate this to is my very sedentary (up until now) lifestyle. I immediately ordered a blood pressure monitor so I can keep an eye on it. Hopefully my new standing desk will help improve things.&lt;/p&gt;
&lt;p&gt;Hopefully this blog post has made you think about how much we as an industry spend sitting down. If you&#39;re in a position to upgrade to a standing desk, then I&#39;d highly recommend it. If you&#39;re not though - then think about standing up in meetings. A lot of us are working from home at the moment, so even meetings are at our computers - we don&#39;t even get the walk from our desk to the meeting room.&lt;/p&gt;
&lt;p&gt;If you already have a standing desk, I&#39;d love to hear your experience with it. Feel free to comment below.&lt;/p&gt;
&lt;p&gt;Happy standing!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Git: Dealing with unrelated changes whilst working on a feature branch</title>
    <link href="https://danclarke.com/git-unrelated-changes-in-feature-branch/"/>
    <updated>2020-09-27T00:00:00Z</updated>
    <id>https://danclarke.com/git-unrelated-changes-in-feature-branch/</id>
    <content type="html">&lt;p&gt;How many times have you been working in a Git feature branch, and come across something in code that you want to change, but isn&#39;t related to the feature your on? Perhaps a code tidyup, or fixing some unrelated warnings?&lt;/p&gt;
&lt;p&gt;What do you do?&lt;/p&gt;
&lt;h3&gt;The long way&lt;/h3&gt;
&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; do it the very long way and interrupt your workflow... Stash/commit all your current changes; switch to the main branch; make these unrelated changes; commit them; switch back to your feature branch; rebase your feature branch back onto the main branch; unstash your changes if you stashed them in step 1; try to remember what you were doing before all this started.&lt;/p&gt;
&lt;h3&gt;The messy history way&lt;/h3&gt;
&lt;p&gt;Or you &lt;em&gt;could&lt;/em&gt; just include it in your feature branch, and leave it as that. The downside of this, is that this change is then part of your feature branch, not its own dedicated commit in your main branch.&lt;/p&gt;
&lt;h1&gt;A better way&lt;/h1&gt;
&lt;p&gt;A better option is to tidy up afterwards. This achieves the same as option 1, but doesn&#39;t interrupt your workflow at the time of doing the work...&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Just commit the unrelated changes into your feature branch. Make sure they&#39;re in dedicated commits.&lt;/li&gt;
&lt;li&gt;Carry on with your feature.&lt;/li&gt;
&lt;li&gt;Once feature is complete - switch to the main branch, and cherry-pick any unrelated commits (ie. commits that shouldn&#39;t be part of your feature branch). At this point, those commits will be duplicated in both branches.&lt;/li&gt;
&lt;li&gt;Rebase your feature branch onto the main branch. This will remove those duplicate commits from your feature branch.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This will leave you with a nice clean graph where your unrelated changes are on your main branch, and then your feature branch will branch off &lt;em&gt;after&lt;/em&gt; those unrelated commits.&lt;/p&gt;
&lt;p&gt;In step 4 - the rebase will remove those duplicate commits from your feature branch because a rebase is just a &amp;quot;replay&amp;quot; of commits. It&#39;s &amp;quot;replaying&amp;quot; your feature branch commits &lt;em&gt;on top of&lt;/em&gt; the main branch commits. So when it tries to replay the duplicate commits, there are no changes to replay because those changes are already in the code - so those duplicate commits will get discarded.&lt;/p&gt;
&lt;h1&gt;If all commits have to go in a feature branch...&lt;/h1&gt;
&lt;p&gt;Your company&#39;s Git policy might be that &lt;em&gt;all&lt;/em&gt; changes have to come from feature branches (perhaps you use PRs for everything). If this is the case, you can create your new unrelated feature branch in step 3 and do exactly the same against this branch rather than the main branch.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;The reason behind this blog post is that for a long time, I&#39;ve been doing the first option (ie. the long way). I&#39;m fairly quick with Git, so it didn&#39;t take that long, but it was still more of an interruption to my workflow than I&#39;d have liked. I very recently realised that it&#39;s much faster to tidy up afterwards with the cherry-pick and rebase technique I describe in this post. At that point, I no longer have my head in the feature I&#39;m working on. And also if there are multiple unrelated changes/commits in the feature branch, then they&#39;ll all get tidied up in one go as part of the same cherry-pick/rebase commands.&lt;/p&gt;
&lt;p&gt;If you&#39;re a Git user, but haven&#39;t got your head around rebasing - definitely spend time trying to understand it. It&#39;s not as complicated as it sounds, and once you get it, it&#39;s actually quite simple. And is one of the most powerful features of Git.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Comparing .NET Mocking Libraries</title>
    <link href="https://danclarke.com/comparing-dotnet-mocking-libraries/"/>
    <updated>2020-09-20T00:00:00Z</updated>
    <id>https://danclarke.com/comparing-dotnet-mocking-libraries/</id>
    <content type="html">&lt;p&gt;There are quite a few different mocking libraries in .NET. &lt;a href=&quot;https://github.com/Moq/moq4/wiki/Quickstart&quot;&gt;Moq&lt;/a&gt; and &lt;a href=&quot;https://nsubstitute.github.io/&quot;&gt;NSubstitute&lt;/a&gt; seem to be by far the main ones I hear that developers use. I&#39;ve used both of these in different projects, and really like them both. Whilst my preference leans towards NSubstitute, I&#39;d be happy using either. Out of interest, I posted a Twitter poll to see what other people preferred, and the results and replies were really interesting!...&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;What&amp;#39;s your favourite mocking library for .NET and why? (reply for &amp;#39;other&amp;#39; - Twitter didn&amp;#39;t allow enough poll options)&lt;/p&gt;&amp;mdash; Dan Clarke (@dracan) &lt;a href=&quot;https://twitter.com/dracan/status/1292113923265695744?ref_src=twsrc%5Etfw&quot;&gt;August 8, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;The winner of the poll is clear - but interestingly, a lot of the replies indicated that they used Moq just because it was the most well known / used, and they hadn&#39;t tried anything else. There was certainly a lot of love for NSubstitute in the replies, commenting that the API is much cleaner.&lt;/p&gt;
&lt;p&gt;I thought it would be interesting to compare some of the leading libraries like for like. So I picked the top three in the poll - namely, Moq, NSubstitute, and FakeItEasy. And I&#39;ll go through some code snippets for some of the more common requirements of a mocking library. Note that I can&#39;t possibly cover every thing in this blog post, but I&#39;ll focus on the features I find myself mostly using.&lt;/p&gt;
&lt;p&gt;But first, what does a mocking library actually do?...&lt;/p&gt;
&lt;h1&gt;What is a mocking library?&lt;/h1&gt;
&lt;p&gt;When writing a test, quite often you want to &lt;em&gt;only&lt;/em&gt; test one particular class and method. But that method might call a dependency that calls into a database, or calls an external service. When writing an &lt;em&gt;integration&lt;/em&gt; test, you may want to include those dependencies in your your test - perhaps using a real database span up in Docker, or an in-memory database. But what if you&#39;re only writing a &lt;em&gt;unit&lt;/em&gt;-test (or the integration &lt;em&gt;can&#39;t&lt;/em&gt; simulate a particular dependency)? How do you call the method you want to test, without it calling into the dependency and therefore making that database call?&lt;/p&gt;
&lt;p&gt;Quite often in .NET codebases, you&#39;ll quite often see classes implement an interface - especially those which call into databases or make external API/service calls. For example, you might have a class called &lt;code&gt;EFStockChecker&lt;/code&gt; which has Entity Framework code that talks to a database to check stock level. This contains &lt;em&gt;implementation details&lt;/em&gt; - ie. it uses the Entity Framework library. This might then implement an interface called &lt;code&gt;IStockChecker&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is this &lt;em&gt;interface&lt;/em&gt; that the rest of the codebase ideally should use - &lt;em&gt;not&lt;/em&gt; the concrete type (implementation). The key point here is that the interface has no concept of Entity Framework, or whatever database technology we might be using. All it does is describe the &lt;em&gt;intent&lt;/em&gt;. This is known as dependency inversion. When programming in this manor, your business logic isn&#39;t concerned with the implementation details of its dependencies. It also means that those dependences are really easy to replace with another implementation when testing.&lt;/p&gt;
&lt;p&gt;Let&#39;s continue with the &lt;code&gt;IStockChecker&lt;/code&gt; example...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IStockChecker&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; sku&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing in the above interface indicates a database or database technology. It&#39;s just about the requirement, not &lt;em&gt;how&lt;/em&gt; that requirement is fulfilled.&lt;/p&gt;
&lt;p&gt;Then I might have some code in my business logic that does this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderHandler&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IStockChecker&lt;/span&gt; _stockChecker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOrderRepository&lt;/span&gt; _orderRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OrderHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IStockChecker&lt;/span&gt; stockChecker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOrderRepository&lt;/span&gt; orderRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _stockChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stockChecker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        _orderRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; orderRepository&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ProcessOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; sku&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_stockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sku&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            _orderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because this code only knows about abstractions (ie. the interfaces), it&#39;s easy to run this code without using the production implementation of those interfaces. I could just create another implementations just for the test that implements those interfaces, but doesn&#39;t call the database. These test implementations are known as &#39;stubs&#39;.&lt;/p&gt;
&lt;p&gt;A mocking library allows you to simulate an interface or abstract type&#39;s implementation. You instantiate a &#39;mock&#39; object of the interface, and tell that mock object what it should return if a method/property is called against that mock. You can also assert that a method/property was or wasn&#39;t called.&lt;/p&gt;
&lt;p&gt;Some people prefer sticking to stubs over mocking libraries, but I personally prefer to avoid creating additional classes when a mocking library can do it for us. A mocking library also adds additional functionality, like as mentioned above - being able to assert/verify that a mocked method was called. You could obviously implement this yourself in your test implementation - but a mocking library can do this for you, so why reinvent the wheel?&lt;/p&gt;
&lt;h1&gt;The basic scenario we&#39;ll be using as an example&lt;/h1&gt;
&lt;p&gt;Let&#39;s start with a very simple scenario. Imagine we&#39;re testing the above-mentioned &lt;code&gt;ProcessOrder&lt;/code&gt; method. Two interfaces are injected into the constructor. The implementation of both of them call out into a database or API (but we don&#39;t care about the data source or implementation), as we&#39;re not testing this. We only care about the logic in our &lt;code&gt;ProcessOrder&lt;/code&gt; method. Eg. How does our &lt;code&gt;ProcessOrder&lt;/code&gt; handle the having stock vs being out of stock.&lt;/p&gt;
&lt;p&gt;Below is an example test to ensure that an order isn&#39;t created if there&#39;s not enough stock (using the Moq mocking library)...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GivenInsufficientStock_DoNotCreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Arrange&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; mockStockChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Mock&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IStockChecker&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; mockOrderRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Mock&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;OrderHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;

    sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ProcessOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;

    mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;It&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Never&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the dependencies are mocked out. The &lt;em&gt;real&lt;/em&gt; implementation would have called out to a database, but the class we&#39;re testing doesn&#39;t know or care about this - it just cares that whatever &amp;quot;IsProductInStock&amp;quot; is, we get a boolean back. It doesn&#39;t care where that comes from, as that&#39;s not it&#39;s responsibility. What &lt;em&gt;is&lt;/em&gt; it&#39;s responsibility is that if &lt;code&gt;IsProductInStock&lt;/code&gt; returns true, the class we&#39;re testing will execute &lt;code&gt;ProcessOrder&lt;/code&gt;. And likewise, if it returns false, it &lt;em&gt;does not&lt;/em&gt; execute &lt;code&gt;ProcessOrder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The above example shows how simple it is to tell the mock object to return &lt;code&gt;false&lt;/code&gt; if &lt;code&gt;IsProductInStock&lt;/code&gt; is called. This bit is just like a stub, but created for you by the mocking library.&lt;/p&gt;
&lt;p&gt;You can also see how we can leverage the mock to &lt;em&gt;verify&lt;/em&gt; that &lt;code&gt;CreateOrder&lt;/code&gt; was or wasn&#39;t called. This bit is like a &#39;spy&#39;, but again, done for you by the mocking library.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(for a description of the different types of test-doubles - eg. dummies, fakes, stubs, spies, mocks - Martin Fowler &lt;a href=&quot;https://martinfowler.com/bliki/TestDouble.html&quot;&gt;has a post&lt;/a&gt; with a short description of each).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In fact, on the NSubstitute homepage, they even try to move away from these specific definitions, and state: &lt;em&gt;&amp;quot;Mock, stub, fake, spy, test double? Strict or loose? Nah, just substitute for the type you need!&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;h1&gt;Comparing Mocking library syntax&lt;/h1&gt;
&lt;p&gt;Okay, now we know what a mocking library does - let&#39;s move onto some syntax comparisons...&lt;/p&gt;
&lt;h2&gt;Mock creation and simple &#39;Returns&#39; Setup with no parameters&lt;/h2&gt;
&lt;p&gt;Let&#39;s start with how we create a mock, then declare a return value for a method or property in a mocked object...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Moq&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; mockStockChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Mock&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IStockChecker&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;TheClassIAmTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// NSubstitute&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; mockStockChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Substitute&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IStockChecker&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;TheClassIAmTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// FakeItEasy&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; mockStockChecker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Fake&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IStockChecker&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;TheClassIAmTesting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NSubstitute certainly seems the cleanest here, not requiring &lt;code&gt;.Object&lt;/code&gt; or a &lt;code&gt;.Setup&lt;/code&gt; call and lambda. Note that Moq &lt;a href=&quot;https://daedtech.com/mock-of-and-mock-get-in-moq/&quot;&gt;also has another syntax&lt;/a&gt;, where you don&#39;t need to use &lt;code&gt;.Object&lt;/code&gt; to get at the mocked object, but if you do this - you have the inverse problem, where you have to do &lt;code&gt;Mock.Get(myMock)&lt;/code&gt; to do any setup.&lt;/p&gt;
&lt;p&gt;Moving forward, I&#39;ll omit the mock instantiation and the SUT call for brevity.&lt;/p&gt;
&lt;h2&gt;&#39;Return&#39; Setup with explicit parameters&lt;/h2&gt;
&lt;p&gt;What about if your method takes parameters? Eg, we know our &lt;code&gt;IsProductInStock&lt;/code&gt; method does take a &#39;sku&#39; parameter...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Moq&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;banana&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// NSubstitute&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// FakeItEasy&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;orange&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is very similar to the previous example - you just specify the parameter value when setting it up.&lt;/p&gt;
&lt;h2&gt;&#39;Return&#39; Setup regardless of parameter values&lt;/h2&gt;
&lt;p&gt;And what if your method takes a parameter, but you don&#39;t care what values are passed to it by the code you&#39;re testing?...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Moq&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;It&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;IsAny&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// NSubstitute&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Arg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Or NSubstitute can also do this to specify that all arguments can be ignored&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ReturnsForAnyArgs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// FakeItEasy&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;A&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ignored&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They all have similar syntax here. Moq uses &lt;code&gt;It.IsAny&amp;lt;&amp;gt;()&lt;/code&gt; syntax, NSubstitute uses &lt;code&gt;Arg.Any&amp;lt;&amp;gt;()&lt;/code&gt;, and FakeItEasy uses &lt;code&gt;A&amp;lt;string&amp;gt;.Ignored&lt;/code&gt;. In NSubstitute, you can also use &lt;code&gt;ReturnsForAnyArgs&lt;/code&gt; and the parameters will be ignored. In the above example, I&#39;ve used the &lt;code&gt;default&lt;/code&gt; keyword when doing this to make it more clear that these values are ignored - I could have put &amp;quot;&amp;quot;, or any other value, but I think using &lt;code&gt;default&lt;/code&gt; makes it more clear.&lt;/p&gt;
&lt;h2&gt;Throwing exceptions&lt;/h2&gt;
&lt;p&gt;Quite often you want to have tests testing how your code handles a dependency throwing an exception. Mocks allow you to simulate exceptions being thrown...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Moq&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStockAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Throws&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;NullReferenceException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// NSubstitute&lt;/span&gt;
mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Throws&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;NullReferenceException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// FakeItEasy&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockStockChecker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsProductInStock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Throws&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;NullReferenceException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of these are very similar to their &lt;code&gt;Returns&lt;/code&gt; counterpart. Just replacing &lt;code&gt;Returns&lt;/code&gt; with &lt;code&gt;Throws&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Verifying a mocked method is or isn&#39;t called&lt;/h2&gt;
&lt;p&gt;Another really useful feature of mocking libraries is that you can &lt;em&gt;spy&lt;/em&gt; on whether a mocked method was called or not. We saw an example of this in the &lt;code&gt;GivenInsufficientStock_DoNotCreateOrder&lt;/code&gt; code snippet earlier in this post, where we verified that &lt;code&gt;CreateOrder&lt;/code&gt; wasn&#39;t called when there was insufficient stock.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Moq&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Defaults to Times.AtLeastOnce&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Never&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Once&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// NSubstitute&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Received&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DidNotReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Received&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Received&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Received&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// FakeItEasy&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MustHaveHappened&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MustNotHaveHappened&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MustHaveHappenedOnceExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MustHaveHappenedTwiceExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
A&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CallTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; mockOrderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MustHaveHappened&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Exactly&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;ve included a few examples above showing how to check for different numbers of calls to a mocked method. This particular method I&#39;ve verifying has no parameters, but all the libraries support verifying the a call was made with specific parameter values, or any parameter values.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;I certainly haven&#39;t touched on all the functionalities that exist in mocking libraries - but rather focused on the features I tend to mostly use. There is plenty of other functionality - eg. working with events, verifying the order of method calls, etc, etc. If there are any comparisons or examples you feel really belong in this post, please do let me know.&lt;/p&gt;
&lt;p&gt;The aim of this post isn&#39;t to say one library is better than another, as when it comes down to it, they all pretty much do the same thing. The purpose of this post is just to provide a side-by-side comparison of some of the syntax, to hopefully give the you a feel of what mock creation and setup looks like across these different libraries.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Multi-page login screens, hidden username fields, and LastPass!</title>
    <link href="https://danclarke.com/hidden-username-fields-and-lastpass/"/>
    <updated>2020-08-23T00:00:00Z</updated>
    <id>https://danclarke.com/hidden-username-fields-and-lastpass/</id>
    <content type="html">&lt;p&gt;I&#39;ve just recently had an issue where I&#39;d created two accounts for the same website - one for myself and one for my wife. I quickly hit a very strange issue where if I logged into my wife&#39;s account, I would end up in my own account! I must admit, I initially jumped to thinking that the website must have some appallingly terrible authentication code. It turns out I was wrong, and it&#39;s something that might be quite common amongst multi-page login screens.&lt;/p&gt;
&lt;p&gt;The website was &#39;Legal and General&#39; life insurance. Yes, I&#39;m at that age now where I have a family and finally realise that I&#39;m not invincible like I used to think when I used to jump out of aeroplanes and ride around on fast motorbikes!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: Given that I&#39;m explicitly naming the company here - I want to point out that this &lt;strong&gt;IS NOT a security issue&lt;/strong&gt;, as you&#39;ll see below. It&#39;s just a gotcha that could stop users from being able to log into their accounts. And it&#39;s one that is probably quite a common one amongst multi-page logins.&lt;/p&gt;
&lt;p&gt;So first of all, I assumed our accounts were linked somehow and there was a bug in their login code (it was the same insurance broker who set up the policies, so it was reasonable to assume there was a link between the accounts). This wasn&#39;t the case after I found out the issue - but it was my first thought.&lt;/p&gt;
&lt;p&gt;I also tried logging into my wife&#39;s account in an incognito window to rule out a browser storage or caching issue. This didn&#39;t fix it either - I still ended up in my account!&lt;/p&gt;
&lt;p&gt;Interestingly, it worked fine from my mobile phone, or from a different browser, or different Chrome profile.&lt;/p&gt;
&lt;p&gt;Then, I hit a big clue! The only browser extension I have enabled in incognito mode is LastPass. So I tried disabling this, and this fixed it!&lt;/p&gt;
&lt;p&gt;Their login pages are as follows...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-08-HiddenUsernameFieldsAndLastPass/landg1.png&quot; alt=&quot;landg1&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-08-HiddenUsernameFieldsAndLastPass/landg2.png&quot; alt=&quot;landg2&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-08-HiddenUsernameFieldsAndLastPass/landg3.png&quot; alt=&quot;landg3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, inspecting the HTML of the last page, shows that there&#39;s a hidden &amp;quot;username&amp;quot; textbox. Which makes sense, as you&#39;ve already entered your username in a previous page - so the username you&#39;re seeing in the last screenshot is just an HTML label. However, an HTML FORM requires an &lt;code&gt;input&lt;/code&gt;, not a &lt;code&gt;label&lt;/code&gt;. Examining the FORM&#39;s HTML, here is a &lt;em&gt;very&lt;/em&gt; simplified version of what it&#39;s doing...&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;anna&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Log in&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the username textbox is hidden! If I unhide it, I see this...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-08-HiddenUsernameFieldsAndLastPass/hiddenfield.png&quot; alt=&quot;hiddenfield&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The text in that screenshot sort of gives away the problem! As you can see, LastPass has populated the hidden field with my username! And it&#39;s this field that gets submitted to the server for authentication, &lt;em&gt;not&lt;/em&gt; the label showing Anna&#39;s username!&lt;/p&gt;
&lt;p&gt;I should point out that the reason this actually logged me into my account, rather than just showing an invalid credential error - is because I was using the same password for both my account and Anna&#39;s. Albeit a VERY strong LastPass generated password. If they had been different passwords, then this would just have shown me an invalid password error because the username and password wouldn&#39;t have matched. Either way, this issue would cause a &lt;em&gt;valid&lt;/em&gt; username/password combo to not let the user sign into their account.&lt;/p&gt;
&lt;h1&gt;Whose Bug is it anyway?&lt;/h1&gt;
&lt;p&gt;So is this a Legal and General&#39;s bug, or LastPass?&lt;/p&gt;
&lt;p&gt;I&#39;d argue that LastPass is at fault here. They shouldn&#39;t be auto-populating hidden fields. But given that they are - as web developers, it&#39;s worth knowing this little gotcha.&lt;/p&gt;
&lt;h1&gt;How can a website fix this?&lt;/h1&gt;
&lt;p&gt;The fix is simple - either don&#39;t use hidden fields in this way, or call them something different than &lt;code&gt;username&lt;/code&gt;, so they don&#39;t get auto-populated.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Cleaner tests with XUnit&#39;s IAsyncLifetime and expression-bodied members</title>
    <link href="https://danclarke.com/cleaner-tests-with-iasynclifetime/"/>
    <updated>2020-08-01T00:00:00Z</updated>
    <id>https://danclarke.com/cleaner-tests-with-iasynclifetime/</id>
    <content type="html">&lt;p&gt;When writing tests, sometimes it can be tempting to dump a bunch of asserts into the same test to avoid duplication across multiple tests...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyTests&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SomeTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SystemUnderTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Okay&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a very simplistic example. Quite often you&#39;d also have to inject mocks into the &lt;code&gt;SystemUnderTest&lt;/code&gt; constructor, and do various other setup logic. You wouldn&#39;t want to duplicate that in each test, as your test class would quickly become huge, with lots of duplicate logic.&lt;/p&gt;
&lt;p&gt;Fortunately, in XUnit, the constructor is called once per test, so you can put the setup logic there, and not duplicate it per test.&lt;/p&gt;
&lt;p&gt;Note that other testing libraries have similar ways to run something once per test - eg. with nunit you can create a &lt;a href=&quot;https://docs.nunit.org/2.5/setup.html&quot;&gt;setup method&lt;/a&gt;. XUnit is my goto - so this post will focus on that.&lt;/p&gt;
&lt;p&gt;To clarify, before I get swamped with comments - this blog post is not insisting that every test &lt;em&gt;can only ever&lt;/em&gt; have a single assert. I think it&#39;s nice if they do - but in some scenarios is does make sense for a single test to have multiple asserts. Either way - this post is just showing a nice pattern I like to use when writing tests.&lt;/p&gt;
&lt;h1&gt;Expression-Bodied Methods for the tests&lt;/h1&gt;
&lt;p&gt;If you read my recent post about &lt;a href=&quot;https://danclarke.com/2020-more-succinct-csharp&quot;&gt;Writing More Succinct C#&lt;/a&gt;, you&#39;ll know that I&#39;m a big fan of &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members#methods&quot;&gt;Expression Bodied Methods&lt;/a&gt;. If your tests are only doing a single assertion (because the &#39;arrange&#39; and &#39;act&#39; are in the setup) - then you can use them to make your tests quite succinct...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyTests&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyResult&lt;/span&gt; _result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;MyTests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SystemUnderTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        _result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;StatusCodeShouldBeOkay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Okay&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AmountShouldBe123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserNameShouldBeBob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whilst this is more code than the single test with multiple assertions - each test is still pretty succinct, and you get the benefit that each test is testing a single specific thing, so any test failures are very clear.&lt;/p&gt;
&lt;h1&gt;Async Setup using IAsyncLifetime&lt;/h1&gt;
&lt;p&gt;Quite often, our methods are &lt;code&gt;async&lt;/code&gt;, and we can&#39;t make constructors async. This is where XUnit&#39;s &lt;a href=&quot;https://bartwullems.blogspot.com/2019/09/xunit-async-lifetime.html&quot;&gt;IAsyncLifetime&lt;/a&gt; comes in. Making your test class implement &lt;code&gt;IAsyncLifetime&lt;/code&gt; will force you to implement two methods: &lt;code&gt;InitialiseAsync&lt;/code&gt; and &lt;code&gt;DisposeAsync&lt;/code&gt;. So taking the above example, if &lt;code&gt;sut.DoSomething()&lt;/code&gt; was actually &lt;code&gt;sut.DoSomethingAsync()&lt;/code&gt; - then we could do this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyTests&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IAsyncLifetime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyResult&lt;/span&gt; _result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SystemUnderTest&lt;/span&gt; _sut&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;MyTests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _sut &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SystemUnderTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...Other initialisation...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InitializeAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; _sut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomethingAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DisposeAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CompletedTask&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;StatusCodeShouldBeOkay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Okay&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AmountShouldBe123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Fact&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UserNameShouldBeBob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;To give credit where credit&#39;s due - I discovered this pattern from a fellow contractor I&#39;m currently working with - &lt;a href=&quot;https://twitter.com/lewishenson&quot;&gt;Lewis Henson&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Combining putting setup in constructor/InitializeAsync, and using expression-bodied-methods - really makes the tests nice and succinct, as you can see in the above examples.&lt;/p&gt;
&lt;p&gt;This pattern is a nice way of organising your tests if you have a bunch of assertions based on a shared setup (&#39;arrange&#39; and &#39;act&#39;). It doesn&#39;t apply in all circumstances - if you have a single &#39;arrange&#39;, &#39;act&#39;, &#39;assert&#39; - then that could just go into the test itself. Or if you have a different setup for each, that&#39;s similar but not the same - you can either create a helper method that takes parameters to abstract out the duplication - or use &lt;a href=&quot;https://andrewlock.net/creating-parameterised-tests-in-xunit-with-inlinedata-classdata-and-memberdata/&quot;&gt;parameterised tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also for completeness - in case anyone&#39;s wondering and hasn&#39;t seen the &lt;code&gt;Should&lt;/code&gt; syntax before - this uses an &lt;em&gt;amazing&lt;/em&gt; library called &lt;a href=&quot;https://fluentassertions.com/&quot;&gt;Fluent Assertions&lt;/a&gt;. It makes writing asserts so much nicer. Definitely worth looking into if you&#39;ve not seen it before! There&#39;s &lt;em&gt;a lot&lt;/em&gt; of different extensions methods in this library which make various different types of assertion very clear and readable.&lt;/p&gt;
&lt;p&gt;Finally, something I discovered recently from a blog post by &lt;a href=&quot;https://twitter.com/WestDiscGolf&quot;&gt;Adam Storr&lt;/a&gt; - &amp;quot;&lt;a href=&quot;https://adamstorr.azurewebsites.net/blog/c-8-using-declarations-with-fluentassertions&quot;&gt;C#8 Using Declarations With FluentAssertions&lt;/a&gt;&amp;quot;... If you &lt;em&gt;do&lt;/em&gt; want to have multiple asserts in the same test, and you want a test failure to show &lt;em&gt;all&lt;/em&gt; of the failing asserts in one go - then you can wrap them in Fluent Assertion&#39;s &#39;Assertion Scopes&#39;...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;AssertionScope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Okay&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Be&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Happy Testing!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>DevOps Tooling for Developers - we&#39;re so spoilt!</title>
    <link href="https://danclarke.com/2020-devops/"/>
    <updated>2020-07-05T00:00:00Z</updated>
    <id>https://danclarke.com/2020-devops/</id>
    <content type="html">&lt;p&gt;Last week I had a catchup meeting with a client about the progress of a platform I&#39;m building for them, and showed them how the &amp;quot;devops&amp;quot; side of things works. Demoing it really struck me how &lt;em&gt;insanely&lt;/em&gt; awesome all the tooling we have nowadays is, and also how much I take it for granted!&lt;/p&gt;
&lt;p&gt;I tweeted about it, but quickly realised as I was writing &lt;a href=&quot;https://twitter.com/dracan/status/1276535270188486656&quot;&gt;the multi-tweet Twitter thread&lt;/a&gt;, that a blog post would be a better fit for what I was trying to say. So here it is!...&lt;/p&gt;
&lt;p&gt;First, let&#39;s start by defining what I mean by the &lt;em&gt;&amp;quot;devops side of things&amp;quot;&lt;/em&gt;. It initially started off with me showing some &lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt; work I&#39;d been doing to deploy the Azure infrastructure in a repeatable manner across environments (dev, UAT, prod). Because both this, and code changes, are triggered by Git pushes - the demo/conversation then evolved into me showing them the whole workflow...&lt;/p&gt;
&lt;p&gt;Ie...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Briefly showing Git&lt;/li&gt;
&lt;li&gt;How pushing to Git triggers Azure DevOps Pipelines&lt;/li&gt;
&lt;li&gt;Deployment approval gateways, allowing acceptance at the click of a button of UAT-&amp;gt;Prod staged deployment&lt;/li&gt;
&lt;li&gt;Traceability - ie. hyperlinks from Azure Board work-items (which I use as a ticketing system and backlog), to Git commits, deployments, etc.&lt;/li&gt;
&lt;li&gt;Terraform Infrastructure deployment&lt;/li&gt;
&lt;li&gt;Azure Application Insights&lt;/li&gt;
&lt;li&gt;Azure Dashboards&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;You don&#39;t have to be a big team&lt;/h1&gt;
&lt;p&gt;Before going into more detail about the above bullet-points, I want to point out that it&#39;s just me working on this client&#39;s project, and I only work for them 1 day each week. I&#39;m a developer, and the main focus of my time is building the software, not the above-mentioned devopsy stuff. Luckily, you get a lot of the above mostly out of the box, and also at zero-cost for small teams! We&#39;re pretty spoilt as developers nowadays!&lt;/p&gt;
&lt;h1&gt;This isn&#39;t just about Microsoft stuff&lt;/h1&gt;
&lt;p&gt;I&#39;m strongly aware that a lot of what I talk about in this post is Microsoft, and very Azure related. The reason for this is because I happen to use Azure DevOps for CI/CD and Azure for cloud hosting. The aim of this post isn&#39;t to market Microsoft products though, and all the technologies I mention have non-Microsoft alternatives - which I do also reference in places in this post. I talk about Terraform, which can just as easily deploy to AWS rather than Azure. There are other CI/CD solutions if you don&#39;t want to use Azure DevOps - eg. &lt;a href=&quot;https://www.jetbrains.com/teamcity/&quot;&gt;TeamCity&lt;/a&gt;, &lt;a href=&quot;https://circleci.com/&quot;&gt;CircleCI&lt;/a&gt;, &lt;a href=&quot;https://www.jenkins.io/&quot;&gt;Jenkins&lt;/a&gt;, and others. There are other diagnostics solutions, which I also discuss below.&lt;/p&gt;
&lt;p&gt;The point I&#39;m trying to make in this post - is that it&#39;s struck me how amazing all this tech is that even one-man bands can leverage. And we have so much choice too!&lt;/p&gt;
&lt;h1&gt;Git -&amp;gt; Automated Builds and Deployment&lt;/h1&gt;
&lt;p&gt;It all starts at Git. Aim to keep &lt;em&gt;everything&lt;/em&gt; in source-control. Obviously primarily this is your codebase, but should ideally also include your infrastructure and deployment setups. This means that you have the history of any changes you make to infrastructure and deployments in source-control alongside your code changes. For example, I mentioned I use Terraform - these files should be in source-control. Azure DevOps Pipelines supports YAML files, which should also be in source control. If you&#39;re using Docker, Kubernetes, etc - put the Dockerfiles, K8S  YAML files, in source control. Try to avoid creating your infrastructure by clicking &#39;Create&#39; in the Azure Portal (or whatever hosting platform you use), and assuming you&#39;ll remember how to recreate it in the future. You don&#39;t have to use Terraform, that&#39;s just my preference - you have &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/overview&quot;&gt;ARM Templates&lt;/a&gt; if using Azure, &lt;a href=&quot;https://www.pulumi.com/&quot;&gt;Pulumi&lt;/a&gt;, or other options.&lt;/p&gt;
&lt;p&gt;Then when pushing these changes, you can have automated processes take over the rest. You&#39;ve probably heard the terms &#39;continuous integration&#39; and &#39;continuous delivery&#39;. This is what this refers to.&lt;/p&gt;
&lt;p&gt;So pushing code changes, can kick off builds and deployments. Infrastructure file changes (in my case Terraform .tf files) can kick off infrastructure updates. Azure Pipelines YAML file changes, can update the deployment workflows (ie. how to build/deploy stuff automatically).&lt;/p&gt;
&lt;p&gt;As you can see - Git really is the &lt;em&gt;trigger&lt;/em&gt; point for things being built/deployed.&lt;/p&gt;
&lt;h1&gt;Traceability&lt;/h1&gt;
&lt;p&gt;I touched upon traceability in the bullet points list above. For this project, I use &lt;a href=&quot;https://azure.microsoft.com/en-gb/services/devops/boards/&quot;&gt;Azure Boards&lt;/a&gt; to manage work that needs doing. So all features, bugs, etc get a work-item (also known as a ticket in some platforms). I have a backlog of items and even though it&#39;s just me, and I&#39;m only working for this client one day a week - I still use sprints, having one sprint per month (ie. 4-5 days). This allows me to plan a vague roadmap to the go-live date. The client has already seen how this works, can create work-items, and we use this for each of our progress catchup meetings.&lt;/p&gt;
&lt;p&gt;In this recent meeting, given I was showing them the Git and CI/CD pipeline, I also then showed how work-items, Git commits, builds and deployments are all hyper-linked to each other. We&#39;re not using Pull Requests for this project, but if we were, the PRs would also be linked. This is so useful if you need to go back and either see why changes were made and what those changes affected, or what work has been done against a particular work item.&lt;/p&gt;
&lt;p&gt;To automatically link a Git commit to a work item, just mention the work item in the commit message. Eg. &amp;quot;#123&amp;quot; in the commit description. Whilst I use Azure DevOps, most other similar solutions work the same way. Eg. GitHub, etc.&lt;/p&gt;
&lt;h1&gt;Monitoring&lt;/h1&gt;
&lt;p&gt;Monitoring is of course very important in your application. Work doesn&#39;t end when you hit the &#39;go-live&#39; date and declare your app is &amp;quot;in production&amp;quot;. Is your app erroring? Are web and database queries performing as expected? Are you just waiting for the end-user to tell you about issues, and assuming everything is hunky-dory if you don&#39;t hear anything? Ideally, you need dashboard, alerts, and logs in place. Dashboards to give you a general &amp;quot;at a glance&amp;quot; overview of the state of your system; alerts to notify you as soon as something goes wrong or isn&#39;t how it should be; and logs to help you drill down and diagnose problems when they arise.&lt;/p&gt;
&lt;p&gt;Sounds complicated? Well yes it can be - but certainly not as complicated as you may think!&lt;/p&gt;
&lt;h2&gt;Application Insights&lt;/h2&gt;
&lt;p&gt;I&#39;m calling out &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview&quot;&gt;Application Insights&lt;/a&gt; because I use Azure and this is my go-to for website telemetry, and it&#39;s pretty mind blowing. However, the point of this blog post isn&#39;t about specific technology stacks, and me saying you should be using X, Y, and Z. There are plenty of other options - eg. &lt;a href=&quot;https://www.datadoghq.com/&quot;&gt;DataDog&lt;/a&gt;, &lt;a href=&quot;https://stackify.com/retrace/&quot;&gt;Retrace&lt;/a&gt;, &lt;a href=&quot;https://www.splunk.com/&quot;&gt;Splunk&lt;/a&gt;, &lt;a href=&quot;https://newrelic.com/&quot;&gt;New Relic&lt;/a&gt;, &lt;a href=&quot;https://raygun.com/&quot;&gt;RayGun&lt;/a&gt; and more. The point of this blog post is to highlight how spoilt we as developers really are with all this &lt;em&gt;amazing&lt;/em&gt; tech.&lt;/p&gt;
&lt;p&gt;But given I use App Insights, let&#39;s talk about that! With minimal work required, you get tons of logs, charts, error and diagnostic information out of the box.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-06-DevOps/AppInsights1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is an example from this blog&#39;s monitoring. You can see there&#39;s a bunch of 404s, which are just bots, but there&#39;s also some SQL failures. Clicking on the count (ie. 114 on the bottom-right) drills in and shows you various information about that failure - including the call stack in the code, SQL queries, etc...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-06-DevOps/AppInsights2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Notice the &amp;quot;Related Items&amp;quot; on the bottom-right of that screenshot. Clicking on those options allows to you see more information about what was happening around the error.&lt;/p&gt;
&lt;p&gt;You can easily write powerful queries against your logs, that both filter and drill down on your logs, or even render custom visualisations...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-06-DevOps/AppInsights4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Notice the query panel in that screenshot with a SQL-like query? This is insanely powerful, and the intuitive intellisense make it even easier to write queries against your logs. You can dump out tables of trace log data, or render charts as in the above screenshot. These charts can easily be pinned to your dashboards (see below).&lt;/p&gt;
&lt;p&gt;All of this pretty much out of the box. All you need to do after creating the App Insights service in Azure, is install a nuget or npm package into your app, and reference the instrumentation key for your App Insight instance. The rest is done for you.&lt;/p&gt;
&lt;h2&gt;Dashboards&lt;/h2&gt;
&lt;p&gt;I mentioned above, dashboards are a great way to give you a general overview of the state of your system. And you have quite a few options here. Obviously, using the built-in dashboarding solution will give you more flexibility, but will also mean you have to have different dashboards in different places. This might be okay though - it depends on your scenario.&lt;/p&gt;
&lt;h4&gt;Azure DevOps Dashboards&lt;/h4&gt;
&lt;p&gt;Azure DevOps supports creating custom dashboard for everything in your DevOps environment. For example, you can create dashboards containing your sprint burndowns and progress; status of builds and deployments; and more.&lt;/p&gt;
&lt;h4&gt;Azure Dashboards&lt;/h4&gt;
&lt;p&gt;Where you can use Azure &lt;em&gt;DevOps&lt;/em&gt; Dashboards for DevOps information, you can also use Azure Dashboards for your Azure services information. This can give you at &lt;em&gt;at a glance&lt;/em&gt; view of your cloud infrastructure. This includes information from your Application Insights (as mentioned above). Infact, most charts you see anywhere in the Azure Portal have a &#39;pin&#39; icon to pin that chart to a dashboard!&lt;/p&gt;
&lt;h4&gt;Grafana&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-06-DevOps/grafana.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://grafana.com/&quot;&gt;Grafana&lt;/a&gt; is an open-source dashboarding solution that can pull data from &lt;em&gt;lots&lt;/em&gt; of different places. And Grafana dashboards look SO GOOD! Where the above-mentioned dashboards are very specific - with Grafana, you create &#39;data sources&#39; which can pull data from various sources - eg. SQL Server, Postgres, Azure, Prometheus, time-series databases like InfluxDB, and much much more. Obviously depending on what you&#39;re dashboarding, you might not get the full power and flexibility of using native dashboards - eg. whilst you can dashboard data from Azure Monitor (including App Insights), you won&#39;t be able to drill into error information within Grafana. I tend to use a combination of both depending on what I&#39;m doing. Because Grafana can pull from different data sources, you can create dashboards combining stuff from Azure, SQL, Prometheus instances, Google Analytics, DataDog, New Relic, etc and view it all in one place.&lt;/p&gt;
&lt;p&gt;You can host Grafana in various ways. Spinning it up in Docker is one way. Which fits nicely if you&#39;re using Kubernetes. But even if you&#39;re not, you could put it on a VM or use Azure Container Instances, etc. They also have a hosted version, which has a free tier for basic usage.&lt;/p&gt;
&lt;p&gt;I&#39;m currently starting playing around with using Grafana to monitor personal stuff, like my todo list counts over time, home broadband usage, Strava stats, Google Analytics all in one place for various sites (eg. this blog, &lt;a href=&quot;http://dotnetoxford.com/&quot;&gt;.NET Oxford&lt;/a&gt; and &lt;a href=&quot;https://www.azureoxford.com/&quot;&gt;Azure Oxford&lt;/a&gt; websites), etc, etc.&lt;/p&gt;
&lt;h1&gt;Pricing&lt;/h1&gt;
&lt;p&gt;You&#39;d have thought all this would be really expensive wouldn&#39;t you? Well, for a small team like I am, other than the Azure hosting itself, it costs nothing. You didn&#39;t mishear that - it doesn&#39;t cost me anything at all! You start to pay when you have a team of over 5 people (and still the first 5 remain free), and/or you need extra build agents, more space for packages, or the &lt;a href=&quot;https://azure.microsoft.com/en-gb/services/devops/test-plans/&quot;&gt;Test Plan&lt;/a&gt; functionality.&lt;/p&gt;
&lt;p&gt;Oh, and automating your infrastructure deployment with ARM, Terraform, Pulumi, etc. - means you can destroy your dev/UAT environments automatically overnight and at weekends, then have them automatically spin up before everyone starts work. This can save you a ton on Azure hosting fees.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;Imagine showing all this stuff to a developer from 10 years ago! I was finding I was taking this stuff completely for granted. Then when I had the meeting I discussed earlier where I was demoing it - it really stuck me - this is insane! Sure, there might be a lot to learn, but most of it isn&#39;t  project-specific, and you can leverage this stuff for any of your projects. Then after that initial learning curve - you&#39;ll then be able to setup a &#39;devops&#39; pipeline/process in less than an hour. Especially when you have all configuration as code - copy a few Azure DevOps YAML files, a few Terraform .tf files, etc - and away you go!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Pair Programming</title>
    <link href="https://danclarke.com/2020-pair-programming/"/>
    <updated>2020-06-20T00:00:00Z</updated>
    <id>https://danclarke.com/2020-pair-programming/</id>
    <content type="html">&lt;p&gt;I&#39;ve recently started a new contract where we do 100% pair-programming. This is the first time I&#39;ve done pair-programming on anywhere near this level. In the past, I&#39;ve only ever paired on an ad-hock basis either to help someone else solve an issue, or vice-versa. This is an 8-hour per day contract, where other than meetings, we&#39;re pairing for all of those 8 hours. Sounds full-on? Well, actually so far, it seems to be working pretty well! Obviously, it makes a &lt;em&gt;huge&lt;/em&gt; difference who you&#39;re working with, and I&#39;ve been lucky that I seem to be getting on well with both developers that I&#39;ve paired with, and we seem to think in the same way and haven&#39;t hit any conflicts so far!&lt;/p&gt;
&lt;p&gt;It&#39;s worth pointing out that this is a 100% remote team, regardless of the current COVID situation. I&#39;ll compare this with the limited experiences I&#39;ve had pairing &#39;in-person&#39; later in the post. I&#39;ll also discuss some tooling that we use.&lt;/p&gt;
&lt;p&gt;I&#39;m going to start by talking about the benefits, which I&#39;m finding, &lt;em&gt;far&lt;/em&gt; outweigh the cons...&lt;/p&gt;
&lt;h1&gt;Knowledge Share and Bus-Factor mitigation&lt;/h1&gt;
&lt;p&gt;I&#39;ve worked with a lot of companies during my career, but I&#39;ve never worked for one where there hasn&#39;t been large areas of code solely known by just one developer. This is obviously bad if/when that developer leaves, either making their code a no-go zone, or causing a lot of lost time whilst other developers have to pick up the code with no context of reasons some decisions were made.&lt;/p&gt;
&lt;p&gt;When work is done in pairs, there&#39;s always more than one person who understands each area of code.&lt;/p&gt;
&lt;h1&gt;Discussions around everything bring better solutions&lt;/h1&gt;
&lt;p&gt;Pair programming takes &lt;a href=&quot;https://en.wikipedia.org/wiki/Rubber_duck_debugging&quot;&gt;rubber-duck debugging&lt;/a&gt; to the next level! Rubber-duck debugging is where a developer explains the problem to someone else (even if it is just to a rubber duck!), and the mere act of articulating the problem helped them realise the issue. With pair-programming, &lt;em&gt;everything&lt;/em&gt; is articulated! Not just bugs and issues, but &lt;em&gt;all&lt;/em&gt; code decisions.&lt;/p&gt;
&lt;p&gt;As part of these discussions, architecture will most certainly come into it. All developers have different backgrounds and ideas, and the discussions around different possible solutions can create much better, more thought-through architectures - saving a lot of time in the future.&lt;/p&gt;
&lt;h1&gt;Two eyes are better than one&lt;/h1&gt;
&lt;p&gt;Two eyes (brains) looking at code being written are far more likely to spot mistakes and potential bugs, than a single developer just typing away. I&#39;m seeing many changes a day being made due to one of us picking up something that the other didn&#39;t spot or think about.&lt;/p&gt;
&lt;h1&gt;Much faster onboarding of new developers&lt;/h1&gt;
&lt;p&gt;Being a new member of the team, it&#39;s really been apparent to me just how much &lt;em&gt;faster&lt;/em&gt; pair programming has helped me pick up both the architecture and codebase. Anything I&#39;m unsure about, I can just ask as part of the conversation we&#39;re already in anyway. I&#39;m not interrupting anyone to ask questions as we&#39;re pairing together anyway. And it&#39;s &lt;em&gt;so&lt;/em&gt; important to ask questions. Always remember, questions create far deeper understanding. Never ever be afraid to ask questions!&lt;/p&gt;
&lt;p&gt;If new developers are just given tasks and left to it - there&#39;s a high chance that it might not conform to the standard design patterns that the team tends to adopt. Unless the codebase really enforces the chosen design patterns, then an codebase that isn&#39;t brand new, is quite likely to be a bit of a mess with various different ways of doing things. This is something that pair-programming can help mitigate against. A new developer can be introduced to the patterns and practices that the team has adopted whilst pair programming. Even if you decide that pair-programming long-term isn&#39;t a good fit for your team - it&#39;s definitely worth doing initially with new starters.&lt;/p&gt;
&lt;h1&gt;Pairing Remotely&lt;/h1&gt;
&lt;p&gt;I mentioned at the start of this post that I&#39;m now working with a 100% remote team. So all the pairing I&#39;m doing is obviously &lt;em&gt;also&lt;/em&gt; 100% remote. Has this been an issue? To be honest, I&#39;ve actually found it easier than pairing in person. Whilst in past companies, I&#39;ve only paired ad-hocly, so perhaps not the same as an official in-person pairing setup - it has always involved one of the pair not being at their own computer. I don&#39;t know about you, but my setup is quite customised. From the hardware where I&#39;m used to my own keyboard/mouse, to software - I use Vi; have various tools and utilities I jump to improve my productivity; my hotkeys are setup how I like them, etc, etc. If I&#39;m using someone else&#39;s machine - it&#39;s a very unproductive experience. Remotely, each developer is using their own machine, and is free to lookup related stuff on the internet, navigate the code separately if they need to, etc.&lt;/p&gt;
&lt;h2&gt;Microsoft Teams&lt;/h2&gt;
&lt;p&gt;We use Teams for communication, where we primarily use it just for calls rather than text. We take turns &amp;quot;driving&amp;quot; and sharing our screens, which seems to work quite well when pairing.&lt;/p&gt;
&lt;p&gt;I mentioned in a recent &lt;a href=&quot;https://danclarke.com/2020-wfh&quot;&gt;blog post about working from home&lt;/a&gt; about the importance of having decent audio. Near the start of the lockdown, I invested in a decent microphone (&lt;a href=&quot;https://www.bluedesigns.com/products/yeti-x/&quot;&gt;Blue Yeti X&lt;/a&gt;), and am so glad I did! I made the investment because I knew I would be working remotely for some time, and would be hosting &lt;a href=&quot;https://www.meetup.com/dotnetoxford/&quot;&gt;.NET Oxford&lt;/a&gt; virtually for some time too. I&#39;m also planning to start a podcast, so this was the ideal opportunity to invest in decent hardware. However, I didn&#39;t realise at the time, I would be literally talking all day long through it for pair programming. When working remotely, audio quality makes a huge different - so is definitely worth the investment.&lt;/p&gt;
&lt;h2&gt;VS Live Share&lt;/h2&gt;
&lt;p&gt;If you&#39;re a Visual Studio or VSCode user, then &lt;a href=&quot;https://visualstudio.microsoft.com/services/live-share/&quot;&gt;Visual Studio Live Share&lt;/a&gt; is definitely worth looking into. My primary IDE is &lt;a href=&quot;https://www.jetbrains.com/rider/&quot;&gt;Rider&lt;/a&gt;, which sadly doesn&#39;t support it - it would certainly be nice if the protocol became an open standard that could work across other IDEs!&lt;/p&gt;
&lt;p&gt;For those that don&#39;t know what Live Share is - it allows one person to &lt;em&gt;host&lt;/em&gt; a session, which others can join. You can then all edit the same code, and you can see what the other person is doing. Think of Google Docs collaboration, but in your editor when coding.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://visualstudio.microsoft.com/wp-content/uploads/2018/11/liveshare-hero-optimized.jpg&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;(image taken from Microsoft&#39;s &lt;a href=&quot;https://visualstudio.microsoft.com/services/live-share/&quot;&gt;website&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can even toggle whether you&#39;re &#39;following&#39; another user, which will automatically navigate your view as they move around the codebase.&lt;/p&gt;
&lt;p&gt;Whilst this is amazing tech, there are downsides. My biggest problem is that as the participant (ie. not the host), you lose &lt;em&gt;a lot&lt;/em&gt; of the IDE functionally. Especially if you use Resharper. I find that intellisense no longer works, and navigation shortcuts stop working, etc. Another issue is that you lose some context of what the other person is doing - you can&#39;t see their intellisense or popup dialogs. So for example, if the other person has a dialogbox up to rename something, and is thinking about what to rename it to - the participant can&#39;t see that dialog. Also, if the other person switches to another application - eg. OneNote, SSMS, LINQpad, etc, you obviously can&#39;t see that either.&lt;/p&gt;
&lt;p&gt;I do find it useful to have vscode with live-share in a secondary monitor though - so I can see the screenshare in my main monitor, giving me full context of what the other person is doing, whilst still being able to edit code in vscode via live-share.&lt;/p&gt;
&lt;p&gt;I think the biggest benefit I get from Live Share rather than screenshare, is that I can scroll around the modified code independently of the driver. When pairing, we tend to take turns driving and being the main person coding. Obviously the person driving, is scrolling up and down and moving around the codebase. This makes it hard to follow along, if I&#39;m trying to read the code. I quite often want to be able to scroll around independently. Whilst I can navigate already pushed code locally on my machine anyway - I obviously can&#39;t see modified code that the other person hasn&#39;t yet pushed. But through Live Share, I can.&lt;/p&gt;
&lt;p&gt;Another advantage to LiveShare over screenshare, is that you can choose font-sizes, etc. Sometimes I find screenshare can be a little bit blurry - especially when our screen aspect-ratios aren&#39;t identical. Looking at the code in VSCode via LiveShare much clearer.&lt;/p&gt;
&lt;h1&gt;Stand up!&lt;/h1&gt;
&lt;p&gt;Another nice advantage of pair programming is you can standup when the other person is &#39;driving&#39;. Of course, if you have a standing desk, you can do this anyway! If you don&#39;t though - it&#39;s worth standing up for a bit when you&#39;re not using the keyboard. Stretch out a bit too whilst you&#39;re stood up. It&#39;s amazing how much of a difference standup up for a bit can make. Do this in meetings too where you&#39;re mainly listening rather than typing. I find when listening in morning standups, if I stand up, I find myself listening far more intently and getting distracted less.&lt;/p&gt;
&lt;h1&gt;The negatives of pair programming&lt;/h1&gt;
&lt;p&gt;I&#39;ve talked &lt;em&gt;a lot&lt;/em&gt; about the positives of pair programming so far, so here are a few negatives...&lt;/p&gt;
&lt;h2&gt;Loss of &#39;The Zone&#39;&lt;/h2&gt;
&lt;p&gt;When solo-programming, I have my headphones on with music, and really get into the zone and find I can focus quite deeply. When pairing, I&#39;m never really getting into this state. That&#39;s not as bad as it sounds, as it&#39;s been replaced by a state of discussion which has plenty of other benefits as described above - but I do miss having periods of time where I can code to music and get into a state of deep work.&lt;/p&gt;
&lt;h2&gt;Not all pairs gets on with each other&lt;/h2&gt;
&lt;p&gt;I&#39;ve been quite fortunate in this new contract. Both developers I&#39;ve paired with so far have not only been &lt;em&gt;really&lt;/em&gt; good developers, but also great to get on with too. It certainly hasn&#39;t felt like a &lt;em&gt;long&lt;/em&gt; day pairing for 8-hours solid with them. However, if you were forced to pair all day with someone you didn&#39;t get on with - after all, some personalities do clash - I can see that not ended well at all. Managers, please bear this in mind when deciding who should work with who!&lt;/p&gt;
&lt;h1&gt;But doesn&#39;t this cost twice as much in resources?&lt;/h1&gt;
&lt;p&gt;When first explaining pair-programming to someone, the usual reaction/question is &lt;em&gt;&amp;quot;but doesn&#39;t this cost twice as much in resources?&amp;quot;&lt;/em&gt;. Hopefully after reading this post, the clear answer is &#39;no&#39;. The quality improvement you get from all the things I&#39;ve discussed; plus mitigating against someone leaving without anyone else knowing their area of code; plus the team bonding, and additional consistency of design patterns you get in the codebase - more than outweigh the cost of two devs working together on work items.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;A couple of years ago, I wrote a blog post about using Pull Requests in the workplace (&lt;a href=&quot;https://danclarke.com/pullrequests-in-the-workplace&quot;&gt;link&lt;/a&gt;). I wrote that post because I had recently started working with a team that used PRs, and this was the first time I&#39;d worked for a company that did this. I&#39;d used PRs for open-source, but not in the workplace itself. It feels like pair-programming takes the benefits I describe in that post to the next level!&lt;/p&gt;
&lt;p&gt;I&#39;m enjoying pairing in this new role, and feel I&#39;ve learnt the architecture, codebase, and the team&#39;s &#39;ways of doing things&#39; much faster than I otherwise would have done. Plus, I feel I&#39;ve gotten to know the team much better than if I&#39;d been working solo on tasks from a backlog (remember, this is a 100% remote team).&lt;/p&gt;
&lt;p&gt;One thing I would say, is that I&#39;d recommend having &lt;em&gt;at least&lt;/em&gt; half-an-hour a day soloing. This allows you to be free to browse the code-base and digest other areas of code.&lt;/p&gt;
&lt;h1&gt;I&#39;d love to hear your experiences!&lt;/h1&gt;
&lt;p&gt;Do you have experience in pairing? Has it been positive or negative? I&#39;d love to hear your thoughts and experiences on this, so please do comment below, or feel free to reach out to me directly.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Writing More Succinct C#</title>
    <link href="https://danclarke.com/2020-more-succinct-csharp/"/>
    <updated>2020-05-10T00:00:00Z</updated>
    <id>https://danclarke.com/2020-more-succinct-csharp/</id>
    <content type="html">&lt;p&gt;When looking at a lot of C# code nowadays, I find myself thinking &lt;em&gt;&amp;quot;wow, that code could be made SO MUCH SMALLER!&amp;quot;&lt;/em&gt;. C# is a very flexible language, allowing you to write clean and functional code, but also very bloated code.&lt;/p&gt;
&lt;p&gt;There are a lot of things in the C# language that can help writing more succinct code - some older (like LINQ), and some which have been added in newer versions of C#. In this blog post, I&#39;ll talk through some of them. But let&#39;s start with a side-by-side example to add a bit of context to what I mean by &lt;em&gt;smaller code&lt;/em&gt;...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-MoreSuccinctCSharp/sidebyside.png&quot; alt=&quot;sidebyside&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Both of the above two methods do the same thing. This is obviously a fabricated example to make a point, but I &lt;em&gt;do&lt;/em&gt; see code like this all the time in real codebases. Look at the right-hand version, and imagine getting this kind of compression, not just in one method, but across your entire codebase. It&#39;s also &lt;em&gt;much&lt;/em&gt; more readable. If you don&#39;t find the right-hand version more readable, then I suspect you&#39;re new to LINQ, and I&#39;d highly recommend learning it if you&#39;re a .NET developer. It&#39;s not new, it has been around since .NET 3.5 (release in 2007), and it is genuinely one of the main reasons I love .NET so much!&lt;/p&gt;
&lt;p&gt;This example covers &lt;em&gt;some&lt;/em&gt; of the topics I&#39;ll discuss in this blog post - eg. LINQ, ternary-if, and expression body members. But I&#39;ll also highlight some other nice ways you can make code more compact - especially if you&#39;re using C#8!&lt;/p&gt;
&lt;p&gt;Now that we&#39;ve looked at an example that adds a bit of context to what this blog post is striving for, let&#39;s dig in...&lt;/p&gt;
&lt;h1&gt;Embrace LINQ!&lt;/h1&gt;
&lt;p&gt;LINQ is AMAZING! It really is one of the main reasons I love .NET so much! It&#39;s been around since .NET 3.5, and the amount of code and complexity that it removes is incredible. The screenshot at the top of this blog post is probably one of the simplest examples of LINQ there is - and yet, look how much code it saved!&lt;/p&gt;
&lt;p&gt;It&#39;s easy to add conditional predicates; sort order; and much more in minimal lines of codes.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;myList
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SomeProperty &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;OrderBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SomeOtherProperty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even easily do &lt;code&gt;GroupBy&lt;/code&gt; and manage aggregate queries.&lt;/p&gt;
&lt;p&gt;I&#39;m not going to delve into LINQ here, as that&#39;s out of the scope of this blog post - however, if you&#39;re using .NET, and not comfortable with LINQ, then definitely spend a bit of time getting good at it. It&#39;ll make a &lt;em&gt;massive&lt;/em&gt; difference to your code.&lt;/p&gt;
&lt;p&gt;One caveat is that LINQ might not be a good fit in performance-critical code, as it can cause more allocations. So be mindful of where you use it. If you&#39;re writing performance-critical code that gets run a lot (eg. in a loop), it&#39;s worth using something like &lt;a href=&quot;https://github.com/dotnet/BenchmarkDotNet&quot;&gt;Benchmark.NET&lt;/a&gt; to measure the performance of that code and know where you allocations are regardless of whether you&#39;re using LINQ or not.&lt;/p&gt;
&lt;h1&gt;Expression body members (C#7)&lt;/h1&gt;
&lt;p&gt;I&#39;m a big fan of expression body members, which allows you to do this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;instead of this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we&#39;ve not only removed the braces, but &lt;em&gt;also&lt;/em&gt; the &lt;code&gt;return&lt;/code&gt; keyword. This is because this is an &lt;em&gt;expression&lt;/em&gt;, rather than a &lt;em&gt;statement&lt;/em&gt;. A statement is a command - ie. &amp;quot;return me a value&amp;quot;, where an expression is just something that evaluates to a value. It might be a single value as it is in the above example, or it could be an algorithm, or it could be the results of a LINQ expression...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token return-type class-name&quot;&gt;IEnumerable&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;OrderItem&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetOrderItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    _orderRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetOrderItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OrderId &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; orderId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the lack of braces and return keyword? Notice how compact and succinct it is, but still very readable.&lt;/p&gt;
&lt;p&gt;I find myself trying to turn as many methods as possible into expression-body members. And thanks to the other things I mention in this post, it&#39;s surprising by how many methods end up being condensed down to a single expression that &lt;em&gt;can&lt;/em&gt; be turned into an expression-body member.&lt;/p&gt;
&lt;h1&gt;Ternary if (?:)&lt;/h1&gt;
&lt;p&gt;Ternary if, also known as an inline-if, allows you to say &amp;quot;if something ? then do this : else do this&amp;quot;.&lt;/p&gt;
&lt;p&gt;For example, this..&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bar
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...can be made much smaller with the ternary if...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bar &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt; bar &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;(and this can be made even small, see next section)&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;Null coalescing operator (??)&lt;/h1&gt;
&lt;p&gt;The above shortened example, can be made even smaller still with the null coalescing operator...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bar &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This says if &lt;code&gt;bar&lt;/code&gt; is not null, then use it, otherwise continue evaluating to the right of the &lt;code&gt;??&lt;/code&gt; operator.&lt;/p&gt;
&lt;h1&gt;Null-coalescing assignment operator (??=) (C#8)&lt;/h1&gt;
&lt;p&gt;If you&#39;re using C#8, then there&#39;s also the &lt;code&gt;??=&lt;/code&gt; operator, which will only evaluate and assign the right-hand value, if the value on the left is null.&lt;/p&gt;
&lt;p&gt;So this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_myValue &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    _myValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InitialiseMyValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...can be shortened to this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;_myValue &lt;span class=&quot;token operator&quot;&gt;??=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InitialiseMyValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;_myValue&lt;/code&gt; isn&#39;t null, then it&#39;ll be left alone, and &lt;code&gt;InitialiseMyValue()&lt;/code&gt; won&#39;t even be called. This is really useful for initialisation.&lt;/p&gt;
&lt;h1&gt;Null conditional operator (?.) (C#6)&lt;/h1&gt;
&lt;p&gt;This is a nice bit of shorthand added into C#6, meaning that you can change this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...into this...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DoSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It basically says if the code to the left of the &lt;code&gt;?.&lt;/code&gt; is null, then stop evaluating anything to the right. So in the example above, if either &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;c&lt;/code&gt; is null, then it won&#39;t do anything, and won&#39;t call &lt;code&gt;DoSomething()&lt;/code&gt;. If you were assigning the above statement to a variable, and either &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;c&lt;/code&gt; was null - then the null will be assigned to that variable.&lt;/p&gt;
&lt;h1&gt;C#8 switch expression&lt;/h1&gt;
&lt;p&gt;Another C#8 one here. I&#39;ve pinched the below example from &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression&quot;&gt;the documentation&lt;/a&gt;...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; orientation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; direction &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Directions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Up    &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Orientation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;North&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Directions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Right &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Orientation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;East&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Directions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Down  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Orientation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;South&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Directions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Left  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Orientation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;West&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a lot cleaner than the typical switch statement we&#39;re used to. And when you start adding &lt;a href=&quot;https://devblogs.microsoft.com/dotnet/do-more-with-patterns-in-c-8-0/&quot;&gt;the power of pattern matching&lt;/a&gt; into the mix, it gets even better!&lt;/p&gt;
&lt;h1&gt;Removing Braces from single-line blocks???&lt;/h1&gt;
&lt;p&gt;First of all, this one is obviously highly controversial! I&#39;m not saying you &lt;em&gt;should&lt;/em&gt; be omitting braces, and there are a lot of people who would find it a cardinal sin. But more and more recently, especially as I look into other languages that don&#39;t require braces - I&#39;m finding them more and more redundant. I mean, we all indent our code don&#39;t we? If not, why not? So why do we need braces at all? Well, for single-line statements - you don&#39;t.&lt;/p&gt;
&lt;p&gt;Omitting the braces in single-line if statements, foreach, where, using, etc. can make code much more compact. Take this example...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-MoreSuccinctCSharp/RemovingBraces.png&quot; alt=&quot;RemovingBraces&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you get used to this, it&#39;s amazing how &lt;em&gt;stretched out&lt;/em&gt; the code on the left looks. It&#39;s really started to make me dislike the concept of braces at all in C#.&lt;/p&gt;
&lt;p&gt;This is now where a lot of C# devs will now be screaming at their monitors, saying things like &lt;em&gt;&amp;quot;But what about the Apple bug?!&amp;quot;&lt;/em&gt;, or &lt;em&gt;&amp;quot;But what if a Python dev who&#39;s used to not having braces, comes in and does this...&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;something&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doSomethingElse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// New method call added by Python dev&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do this, your IDE/Editor is going to SCREAM at you. Unless of course, you&#39;re coding in notepad. Also, most editor&#39;s default setup is to autoformat on &lt;code&gt;;&lt;/code&gt; or &lt;code&gt;}&lt;/code&gt; - so this&#39;ll be very quickly auto-indented. Obviously, you&#39;ll get the rare exception where someone has disabled all this functionality, or someone&#39;s using an editor that doesn&#39;t lint - but in the most case nowadays, you&#39;d have to program blindfolded to get caught by this.&lt;/p&gt;
&lt;p&gt;Re. &lt;a href=&quot;https://blog.codecentric.de/en/2014/02/curly-braces/&quot;&gt;the Apple bug&lt;/a&gt;, I struggle with the fact that one person wrote a bug a long time ago - and from now on, everyone has to pad out their code like shown in the screenshot above. If we had to change the way we write code every time someone writes a bug - we wouldn&#39;t be allowed to touch the keyboard.&lt;/p&gt;
&lt;p&gt;Many years ago, I was a C/C++ developer, where it was good practice to always wrap single-line blocks in braces. There was good reason for this though. C/C++ has a concept called &lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/preprocessor/macros-c-cpp?view=vs-2019&quot;&gt;macros&lt;/a&gt;. When macros are used in code, it might &lt;em&gt;look&lt;/em&gt; like a single-line if statement, however, when expanded out by the preprocessor, it might actually be a multiline if statement. So it can be dangerous if you don&#39;t wrap all your blocks in braces. This isn&#39;t the case in C# though.&lt;/p&gt;
&lt;p&gt;Whilst I prefer omitting braces and find them redundant (after all, we indent our code anyway, so why do we need braces?!) - I have recently found a genuine reason why this perhaps isn&#39;t a good idea. In fact, I found it whilst writing this blog post...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-MoreSuccinctCSharp/StackOverflowAnswer.png&quot; alt=&quot;StackOverflowAnswer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(&lt;a href=&quot;https://stackoverflow.com/questions/2125066/is-it-a-bad-practice-to-use-an-if-statement-without-curly-braces/2125207#2125207&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;The two comments at the bottom of that screenshot pretty much echoed my thoughts. Most other arguments against this felt like the issue was sloppy indentation, not an issue with omitting the braces. However, the above example isn&#39;t lazy programming, because it &lt;em&gt;looks&lt;/em&gt; like it&#39;s indented correctly.&lt;/p&gt;
&lt;p&gt;Even so, as I mentioned above - your IDE/editor is most likely going to bark at you and probably even fix the indentation for you automatically.&lt;/p&gt;
&lt;p&gt;So yes, this makes a big difference in code size, but you have to be careful not to introduce the above-mentioned bug. So this section isn&#39;t advice on either way - it&#39;s just my thoughts on the pros and cons. As you can see from the screenshot at the start of this section, it does make quite a difference - just be careful if you decide to do this. Using some of the other techniques in this blog post can help eliminated if-else blocks altogether anyway.&lt;/p&gt;
&lt;p&gt;Also, remember that switch case blocks do not require braces even for multi-line blocks.&lt;/p&gt;
&lt;h1&gt;Remove unnecessary local variables&lt;/h1&gt;
&lt;p&gt;Another one I see a lot is code like this..&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-MoreSuccinctCSharp/removeunnecessarylocalvariables.png&quot; alt=&quot;removeunnecessarylocalvariables&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are a lot of unnecessary local variables here. We can already see what each method-call is doing from the method names, and the next method-calls are then just members of the previous method. These can be nicely chained together instead of introducing local variables...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-MoreSuccinctCSharp/removeunnecessarylocalvariablesv2.png&quot; alt=&quot;removeunnecessarylocalvariables&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s much cleaner - especially when you get used to using fluent syntax. And as a bonus, we were then able to turn it into an expression-body-method, removing the braces and the &lt;code&gt;return&lt;/code&gt; keyword!&lt;/p&gt;
&lt;p&gt;Although do bear in mind that neither of these examples have error checking, and rely on exceptions being caught by calling methods. It might be that you need to store a local variable to do error checking, or to write log entries. So I&#39;m certainly not saying to &lt;em&gt;always&lt;/em&gt; avoid local variables - just do it intentionally, rather than blindly assigning every single method call to a local variable. The above is just an example to make a point.&lt;/p&gt;
&lt;h1&gt;Use var&lt;/h1&gt;
&lt;p&gt;Spot the duplication...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Dictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; myVariable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Dictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isn&#39;t this much better?...&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; myVariable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Dictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember that &lt;code&gt;var&lt;/code&gt; is still statically typed. There&#39;s no difference in what the two lines above get compiled down to.&lt;/p&gt;
&lt;p&gt;The only argument against using &lt;code&gt;var&lt;/code&gt; I&#39;ve heard is that it&#39;s less clear. However, I&#39;ve used it everywhere for years, and have never once wished I&#39;d been more clear and used an explicit type. It&#39;s pretty much always clear from the context, and if not, just hover your mouse over it to find out what type it is.&lt;/p&gt;
&lt;h1&gt;Moving towards Functional Programming&lt;/h1&gt;
&lt;p&gt;It&#39;s clear that as the C# language evolves, we&#39;re moving more and more into a functional style of programming. LINQ was added in .NET 3.5 - and the later versions of C# are adding more and more ways to help us write code in a more functional manner, as you can see from the examples above. As we start to think in this style of programming, it becomes easier to adopt other parts of functional programming. For example, pure functions. A pure function is a function that for each input (ie. function arguments), it&#39;ll &lt;em&gt;always&lt;/em&gt; have the same return value. A pure function itself with have no side-effects, and not change state outside of the function (eg. no database or API calls, no changing external properties, etc). With smaller, more compact expression-body methods, this becomes easier. And also easier to write tests against.&lt;/p&gt;
&lt;h1&gt;Debugging&lt;/h1&gt;
&lt;p&gt;One downside to more compact and functional code, is that it can sometimes be harder to debug. There&#39;s less code to put breakpoints against, and you can&#39;t add watches to variables that you don&#39;t have because you&#39;ve avoided local variables. However, I&#39;d much rather have a nice compact readable codebase throughout the &lt;em&gt;entire&lt;/em&gt; codebase, and very occasionally have to add a temporarily variable assignment whilst I debug something. This is much more favourable than the entire codebase being massively bloated. Besides, modern debuggers do help, allowing you to debug into LINQ expressions and lambdas anyway.&lt;/p&gt;
&lt;p&gt;You&#39;ll probably also find that you have less reason to debug, as a more functional codebase is likely to have fewer bugs and is easier to reason about. More pure functions also means that it&#39;s easier to run your code via a test instead, or just copy that method into a &lt;a href=&quot;https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop&quot;&gt;REPL&lt;/a&gt; where you can run it standalone.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;I&#39;ve given a lot of different examples in this blog post of how to make your code much more succinct and compact, whilst still being very readable (in some cases, gaining far more readability!). It&#39;s when these techniques get combined, not just in one method, but across your entire codebase, that you really start to see big differences. Everywhere I look at the moment, I&#39;m seeing C# code that could be made so much smaller than it is. Hopefully after reading this blog post, you join me in quest to vanquish bloated C# from your codebases!&lt;/p&gt;
&lt;p&gt;There&#39;s an &lt;a href=&quot;https://www.slideshare.net/ScottWlaschin/c-light&quot;&gt;interesting slideshow&lt;/a&gt; by &lt;a href=&quot;https://www.linkedin.com/in/scottwlaschin/&quot;&gt;Scott Wlaschion&lt;/a&gt; about a made-up language called &lt;em&gt;C# Light&lt;/em&gt;, where he discusses some of the bloat in the C# language. As he warns in the initial slides, it&#39;s only for those of you who are open-minded! I certainly ended the slides thinking that we were just left with F# - which isn&#39;t a bad thing! Learning F# is what made me start to notice how bloated C# was in the first place!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The First Virtual .NET Oxford meetup</title>
    <link href="https://danclarke.com/2020-first-virtual-dotnetoxford/"/>
    <updated>2020-04-27T00:00:00Z</updated>
    <id>https://danclarke.com/2020-first-virtual-dotnetoxford/</id>
    <content type="html">&lt;p&gt;This week we had our very first attempt at a &lt;em&gt;virtual&lt;/em&gt; &lt;a href=&quot;https://www.meetup.com/dotnetoxford&quot;&gt;.NET Oxford&lt;/a&gt; event! And it seemed to go pretty well! We were joined by &lt;a href=&quot;https://twitter.com/markrendle&quot;&gt;Mark Rendle&lt;/a&gt; to talk all about &lt;a href=&quot;https://github.com/dotnet/roslyn&quot;&gt;Roslyn&lt;/a&gt;! We also had a 10-minute lightning talk by Megan who works at our sponsor company, &lt;a href=&quot;https://corriculo.co.uk/&quot;&gt;Corriculo Recruitment&lt;/a&gt; talking about the state of the the job market due to COVID-19.&lt;/p&gt;
&lt;p&gt;So I thought I&#39;d write this blog post to talk about it, as well as the decisions made and the reasons behind those decisions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-FirstVirtualDotNetOxford/GalleryPhoto.png&quot; alt=&quot;GalleryPhoto&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One of the key things that was very important to me, was that it &lt;em&gt;felt&lt;/em&gt; like a physical meetup, and not a one-way stream, or everyone individually watching a YouTube video. For me, the whole point of a user-group is the social interaction with like-minded people. Also, getting to meet and chat the speaker in person (even if it is via a video-conferencing medium).&lt;/p&gt;
&lt;p&gt;So because of this, here are a few decisions I made...&lt;/p&gt;
&lt;h2&gt;Not live-streaming to YouTube&lt;/h2&gt;
&lt;p&gt;A few user-groups I&#39;ve seen have used Zoom and set it up to live-stream to YouTube. I can understand the appeal of this, potentially opening it up to more people. However, for me, if someone wants to attend the event, I want them to &lt;em&gt;attend&lt;/em&gt; and actually be there. Joining via Zoom feels much more like people are attending rather than invisibly watching in secret via YouTube.&lt;/p&gt;
&lt;p&gt;It was recorded anyway, and I edited it and &lt;a href=&quot;https://www.youtube.com/watch?v=22EoxKW582k&quot;&gt;published it to YouTube&lt;/a&gt;. So anyone that couldn&#39;t make it live in Zoom, will still be able to watch the content.&lt;/p&gt;
&lt;p&gt;Another issue I see with live streams to YouTube, is you get all the chat at the start before the meetup officially begins. Because I edited the recording, I was able to get rid of everything but the main talks, add transitions, etc. I even considered overlaying the messages from the group chat window onto the video at the times they were made - but didn&#39;t get the time to do that.&lt;/p&gt;
&lt;h2&gt;Nagging everyone to turn on webcams&lt;/h2&gt;
&lt;p&gt;I also tried to persuade everyone to turn on their webcams - both in my group emails about the event, and also the intro-talk. And I was very impressed with the number of people that did! In a &lt;em&gt;physical&lt;/em&gt; meetup, everyone can see everyone else - no-one is invisible. The speaker can engage with their audience. And the audience engage with the speaker, and get involved. Whilst the audience can still get involved via typing in the chat window or via audio - it&#39;s &lt;em&gt;much&lt;/em&gt; better with both webcams and mics turned on, feeling far more &#39;in person&#39;.&lt;/p&gt;
&lt;p&gt;And the speaker even tweeted afterwards saying how much of a difference this made...&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;For &lt;a href=&quot;https://twitter.com/dotnetoxford?ref_src=twsrc%5Etfw&quot;&gt;@dotnetoxford&lt;/a&gt; most of the attendees turned their webcams on and it was so much better being able to see people&amp;#39;s faces while I was talking!&lt;/p&gt;&amp;mdash; Mark Rendle &#92;n (@markrendle) &lt;a href=&quot;https://twitter.com/markrendle/status/1252717542512177159?ref_src=twsrc%5Etfw&quot;&gt;April 21, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h2&gt;Zoom as the platform of choice&lt;/h2&gt;
&lt;p&gt;There were a few platforms I could have gone with - Zoom, Microsoft Teams, Twitch, etc.&lt;/p&gt;
&lt;p&gt;I haven&#39;t used Twitch, but from what I gather, it&#39;s more for one-way live streaming. The audience can participate via a chat window - but not to the extend that I wanted with webcams and gallery views. If I&#39;ve got this wrong, please let me know!&lt;/p&gt;
&lt;p&gt;Another option was Microsoft Teams. I really like Teams within a work environment. It also has something new called &lt;a href=&quot;https://docs.microsoft.com/en-us/microsoftteams/teams-live-events/what-are-teams-live-events&quot;&gt;Teams Live Events&lt;/a&gt;, and you can find a good &lt;a href=&quot;https://www.youtube.com/watch?v=m85ZGXF06vk&quot;&gt;video&lt;/a&gt; from James Montemagno demoing this. So Teams would have been my second choice. I haven&#39;t played with the Teams Live yet - and I&#39;m not sure whether you get the same level of audience participation as with Zoom, or if it&#39;s more like Twitch - it&#39;s certainly on my list of things to look more into!&lt;/p&gt;
&lt;p&gt;I&#39;ve seen Teams used for bigger events like the MVP Summit, which very quickly switched from a physical event to an online event due to COVID-19. This didn&#39;t use the Teams Live functionality, and definitely felt very collaborative, and it was &lt;em&gt;really&lt;/em&gt; impressive the speed that Microsoft turned everything around and made this big event virtual. I did see lots of MVPs having issues getting access though - which is something I was concerned about - I didn&#39;t want to have to do IT support trying to work out why people couldn&#39;t access the meetup. I believe Teams &lt;em&gt;Live&lt;/em&gt; shouldn&#39;t have this issue as you can view anonymously. So basically, this might work well, but I need to do more investigations and play around with it.&lt;/p&gt;
&lt;p&gt;I&#39;d already used Zoom for meetings, and knew it would work well. There&#39;s also functionally in there that I wanted to try out that Teams doesn&#39;t have - for example, Breakout Rooms (see below).&lt;/p&gt;
&lt;p&gt;Note that I wrote the above before we had had the meetup. And after having the event, I can say that Zoom worked perfectly! We&#39;ll probably stick with Zoom for the near future, as we have no reason to change. To move to something else, would be changing platform for the sake of changing.&lt;/p&gt;
&lt;p&gt;Also, whilst the default max attendee limit is 100, our sponsors, &lt;a href=&quot;https://corriculo.co.uk/&quot;&gt;Corriculo Recruitment&lt;/a&gt; are paying for a Pro licence with an additional guests add-on, allowing up to 500 participants.&lt;/p&gt;
&lt;p&gt;The only small issue I had with Zoom is the inability to &#39;react&#39; to group messages. Ie. like, thumbsup, etc. This would be a really useful addition.&lt;/p&gt;
&lt;h1&gt;Recording&lt;/h1&gt;
&lt;p&gt;There were a few options for recording - Zoom local recording, Zoom cloud recording, or streaming to another platform like YouTube. As mentioned above, I didn&#39;t want to live stream, but I assume I could have streamed privately to YouTube. I ended up just going with the Zoom cloud recording, hoping that it didn&#39;t go over the 1GB limit. The final video ended up being 1.7GB, but luckily Zoom didn&#39;t stop recording, or even stop me from downloading it. I just got an email saying that I&#39;d exceeded my limit. Next time, I might look into streaming privately to YouTube.&lt;/p&gt;
&lt;p&gt;After the event, I then downloaded the mp4 from Zoom, and edited it in &lt;a href=&quot;https://www.techsmith.com/video-editor.html&quot;&gt;Camtasia&lt;/a&gt;. I removed the chat before the event started, but decided to keep the chat during the break and afterwards, as there was some really good conversation in there!&lt;/p&gt;
&lt;p&gt;Camtasia also has a nice feature when publishing to YouTube to automatically generate a table of contents based on markers. So whilst the same video includes my intro talk, Mark&#39;s talk, Megan&#39;s lightning talk, and the chat during the break and after the event - there&#39;s a nice clickable table of contents in the YouTube description to jump to each section.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/22EoxKW582k&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;h1&gt;Prize Draw app&lt;/h1&gt;
&lt;p&gt;When .NET Oxford first started over 3 years ago, I wrote a WPF prize-draw app which connected to the Meetup.com API to pull down RSVPs to draw from. I&#39;ve recently added Zoom integration to this (&lt;a href=&quot;https://www.danclarke.com/2020-prizedraw-zoom&quot;&gt;see my blog post with details&lt;/a&gt;). Given that my integration involves writing webhook events to Azure Table Storage - I thought after the event, that it might be interesting to dump out a chart of the number of people in the meeting throughout the course of the event. So I wrote a small bit of code in &lt;a href=&quot;https://www.linqpad.net/&quot;&gt;LINQPad&lt;/a&gt; to generate a chart...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-FirstVirtualDotNetOxford/Chart.png&quot; alt=&quot;Chart&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On the night, the prize-draw app worked perfectly. Although, I need to add a black-list, as a couple of staff from our sponsors were drawn, so we had to redraw! As I write this, another meetup (&lt;a href=&quot;https://www.meetup.com/dotnetsouthwest/events/270194696/&quot;&gt;.NET South West&lt;/a&gt;) will be trialling my prize draw app tonight using the Zoom integration. Looking forward to seeing how it goes at another user-group!&lt;/p&gt;
&lt;h1&gt;Breakout Rooms&lt;/h1&gt;
&lt;p&gt;During the break, we trialled using Zoom&#39;s &lt;a href=&quot;https://support.zoom.us/hc/en-us/articles/206476093-Enabling-breakout-rooms&quot;&gt;Breakout rooms&lt;/a&gt; functionality. This allows the host to create X number of rooms, and have Zoom automatically divide everyone into those rooms. Joining is optional for the participant - they can stay in the main room if they choose (or go back and forth between the main room and their breakout room). This is great because in a room of 100+ people, there&#39;ll probably only be a handful of people getting involved in the conversation. Having smaller rooms of 5-10 people more simulates a physical event where everyone &amp;quot;breaks out&amp;quot; into smaller groups to mingle.&lt;/p&gt;
&lt;p&gt;We got good feedback about this - however interestingly, I created a Zoom poll at the end to see what everyone wanted to do for the &amp;quot;virtual pub&amp;quot; afterwards...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danclarke.com/images/2020-04-FirstVirtualDotNetOxford/PollResults.png&quot; alt=&quot;PollResults&quot; /&gt;&lt;/p&gt;
&lt;p&gt;So out of the 13 people who said they were staying, only 2 wanted to use breakout rooms. Bear in mind that at the time, no-one knew that only 13 people were hanging around, so breakout rooms probably wouldn&#39;t have made sense anyway. But this poll might have looked very different if it had been asked about the break, rather than the after-event chat. I think I might send out a Survey Monkey poll asking for feedback on both this, and the event in general.&lt;/p&gt;
&lt;h1&gt;Zoom Invite Link and Meetup.com&lt;/h1&gt;
&lt;p&gt;One final point to mention for those that are doing similar, is once the event starts, attendees can no longer RSVP on Meetup. Which means that they won&#39;t be able to see the Meetup online Zoom invite URL! We experienced this first-hand with none other than the speaker himself! Oops! I hadn&#39;t twigged that he might not have RSVPed, and wouldn&#39;t have the invite link. Luckily, in the chat beforehand, someone pointed out that he had asked on Twitter for it - so I quickly DMed him the invite link. So it&#39;s worth making sure that even with all the other things you have to think about when kicking off your very first virtual meetup - ensure the speaker knows the address of the venue!&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;So, in summary - it worked really well! Everyone seemed to have a great time, and we got really good feedback! We even got feedback from a couple of people who live abroad, saying that they hope we keep up a virtual presence in some form after the lock-down ends, so they can continue participating!&lt;/p&gt;
</content>
  </entry>
</feed>
