<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>The Angry Dev</title><link>https://www.darrenhorrocks.co.uk/</link><description>Recent content on The Angry Dev</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><lastBuildDate>Tue, 07 Oct 2025 10:08:00 +0100</lastBuildDate><item><title>Windows 10 Is Dying, but Gaming (and working) on Linux Is Easy</title><link>https://www.darrenhorrocks.co.uk/windows-10-dying-but-gaming-and-working-linux-easy/</link><pubDate>Tue, 07 Oct 2025 10:08:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/windows-10-dying-but-gaming-and-working-linux-easy/</guid><description>&lt;p>Microsoft is officially ending support for Windows 10 in October 2025, and for millions of PC users, that sounds like bad news, because you may not have the right hardware for Windows 11, or you may not want all of your personal data vacuumed up by Microsoft. But here&amp;rsquo;s the twist: you don&amp;rsquo;t have to buy new hardware or upgrade to Windows 11 just to keep playing your favorite games.&lt;/p>
&lt;p>Thanks to modern Linux distributions like Ubuntu, gaming on Linux is now shockingly simple. You can install it in under an hour, play your Steam and Epic Games library, and even do your everyday work&amp;hellip; All without touching the command line.&lt;/p>
&lt;h2 id="why-linux-is-perfect-for-gaming-and-work-in-2025">Why Linux Is Perfect for Gaming and Work in 2025&lt;/h2>
&lt;p>Linux has come a long way. Thanks to tools like &lt;strong>Steam&amp;rsquo;s Proton&lt;/strong> and &lt;strong>Heroic Games Launcher&lt;/strong>, it can now run most popular PC games, including those from Windows only platforms (with exceptions for games that require kernel mode anti-cheat such as Fortnite and Apex Legends for now). You also get powerful office software, web browsers, and creative tools which are all free and easy to install.&lt;/p>
&lt;p>Let&amp;rsquo;s go step by step through how to set up a complete Linux system that&amp;rsquo;s ready for both gaming and work.&lt;/p>
&lt;h2 id="step-1-install-ubuntu">Step 1: Install Ubuntu&lt;/h2>
&lt;p>If you&amp;rsquo;re new to Linux, start with &lt;strong>Ubuntu&lt;/strong>. It&amp;rsquo;s one of the most beginner friendly versions, designed for everyday users and gamers alike.&lt;/p>
&lt;p>&lt;strong>How to install Ubuntu:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Go to &lt;a href="https://ubuntu.com/download/desktop">ubuntu.com&lt;/a>.&lt;/li>
&lt;li>Download the latest &lt;strong>LTS (Long Term Support)&lt;/strong> version as this gets updates for five years before you update to the new LTS.&lt;/li>
&lt;li>Use a tool like &lt;strong>Rufus&lt;/strong> (on Windows) or &lt;strong>balenaEtcher&lt;/strong> (on Mac/Linux) to create a USB installer.&lt;/li>
&lt;li>Boot your computer from the USB stick and select &lt;strong>&amp;ldquo;Try or Install Ubuntu.&amp;quot;&lt;/strong>&lt;/li>
&lt;li>Follow the simple on screen installation steps.&lt;/li>
&lt;/ol>
&lt;p>You can even try Ubuntu before installing it, just choose &lt;strong>&amp;ldquo;Try Ubuntu&amp;rdquo;&lt;/strong> to see how it feels (other beginner friendly Linux distributions to look at are &lt;a href="https://linuxmint.com/download.php">Linux Mint&lt;/a> or &lt;a href="https://zorin.com/os/download/#core">ZorinOS Core&lt;/a>, both of which have a much more Windows like appearance).&lt;/p>
&lt;h2 id="step-2-keep-ubuntu-updated-no-command-line-needed">Step 2: Keep Ubuntu Updated (No Command Line Needed)&lt;/h2>
&lt;p>After installation, click the &lt;strong>Software Updater&lt;/strong> icon in the app menu or dock.
Ubuntu will automatically check for updates, just click &lt;strong>Install Now&lt;/strong> when prompted.&lt;/p>
&lt;p>Your system stays secure and up to date, no manual steps or terminal commands required.&lt;/p>
&lt;h2 id="step-3-install-steam-on-linux-for-gaming">Step 3: Install Steam on Linux for Gaming&lt;/h2>
&lt;p>Steam runs natively on Linux, and with &lt;strong>Proton&lt;/strong>, you can play thousands of Windows only titles.&lt;/p>
&lt;p>&lt;strong>To install Steam:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Open the &lt;strong>App Center&lt;/strong> (orange shopping bag icon).&lt;/li>
&lt;li>Search for &lt;strong>Steam&lt;/strong>.&lt;/li>
&lt;li>Click &lt;strong>Install&lt;/strong>, then &lt;strong>Open&lt;/strong> when it&amp;rsquo;s ready.&lt;/li>
&lt;li>Sign in to your Steam account.&lt;/li>
&lt;/ol>
&lt;p>Once inside Steam:&lt;/p>
&lt;ul>
&lt;li>Go to &lt;strong>Settings → Compatibility&lt;/strong>.&lt;/li>
&lt;li>Check &amp;ldquo;&lt;strong>Enable Steam Play for all other titles&lt;/strong>.&amp;rdquo;&lt;/li>
&lt;li>Select the latest version of &lt;strong>Proton&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>Now you can install and play your Steam library. Games like &lt;em>Baldur&amp;rsquo;s Gate 3&lt;/em>, &lt;em>Cyberpunk 2077&lt;/em>, &lt;em>No Man&amp;rsquo;s Sky&lt;/em>, and &lt;em>Elden Ring&lt;/em> work great on Linux.&lt;/p>
&lt;h2 id="step-4-use-heroic-games-launcher-for-epic-games-and-gog-titles">Step 4: Use Heroic Games Launcher for Epic Games and GOG Titles&lt;/h2>
&lt;p>If you have games from &lt;strong>Epic Games&lt;/strong>, &lt;strong>GOG&lt;/strong>, or &lt;strong>Amazon Prime Gaming&lt;/strong>, the &lt;strong>Heroic Games Launcher&lt;/strong> makes them playable on Linux too.&lt;/p>
&lt;p>&lt;strong>To install Heroic:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>Visit &lt;a href="https://heroicgameslauncher.com/">heroicgameslauncher.com&lt;/a>.&lt;/li>
&lt;li>Download the &lt;strong>Ubuntu (.deb)&lt;/strong> file via github (amd64 deb version) or, &lt;a href="https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/download/v2.18.1/Heroic-2.18.1-linux-amd64.deb">download the latest version at the time of writing by clicking this link&lt;/a>.&lt;/li>
&lt;li>Double click the file to open it in the App Center, then click &lt;strong>Install&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Open Heroic from your applications menu, log into your Epic or GOG account, and download your games.
Heroic handles the compatibility automatically, just click &lt;strong>Play&lt;/strong>.&lt;/p>
&lt;h2 id="step-5-install-google-chrome-on-ubuntu">Step 5: Install Google Chrome on Ubuntu&lt;/h2>
&lt;p>Ubuntu includes Firefox by default, but if you prefer &lt;strong>Google Chrome&lt;/strong>, it&amp;rsquo;s just as easy to install:&lt;/p>
&lt;ol>
&lt;li>Go to &lt;a href="https://www.google.com/chrome/">google.com/chrome&lt;/a> using Firefox.&lt;/li>
&lt;li>Click &lt;strong>Download Chrome&lt;/strong>.&lt;/li>
&lt;li>Choose the &lt;strong>.deb for Ubuntu&lt;/strong> option.&lt;/li>
&lt;li>Once downloaded, double click and select &lt;strong>Install&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Chrome will appear in your app menu and will update automatically through Ubuntu&amp;rsquo;s software updater.&lt;/p>
&lt;h2 id="step-6-use-libreoffice-for-work-or-install-it-if-missing">Step 6: Use LibreOffice for Work (or Install It If Missing)&lt;/h2>
&lt;p>Ubuntu usually includes &lt;strong>LibreOffice&lt;/strong>, a full featured office suite compatible with Microsoft Word, Excel, and PowerPoint files.&lt;/p>
&lt;p>To check:&lt;/p>
&lt;ol>
&lt;li>Open the &lt;strong>App Menu&lt;/strong> and look for &lt;strong>LibreOffice Writer&lt;/strong> (for documents) or &lt;strong>Calc&lt;/strong> (for spreadsheets).&lt;/li>
&lt;li>If it&amp;rsquo;s not there, open the &lt;strong>App Center&lt;/strong>, search for &lt;strong>LibreOffice&lt;/strong>, and click &lt;strong>Install&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>LibreOffice supports &lt;code>.docx&lt;/code>, &lt;code>.xlsx&lt;/code>, and &lt;code>.pptx&lt;/code> formats and even exports to PDF. It&amp;rsquo;s fast, free, and ideal for daily work.&lt;/p>
&lt;h2 id="step-7-add-essential-apps-for-everyday-use">Step 7: Add Essential Apps for Everyday Use&lt;/h2>
&lt;p>Here are a few more great apps available directly from the App Center:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Discord&lt;/strong> – Chat and game with friends.&lt;/li>
&lt;li>&lt;strong>VLC Media Player&lt;/strong> – Watch videos in any format.&lt;/li>
&lt;li>&lt;strong>OBS Studio&lt;/strong> – Stream or record gameplay.&lt;/li>
&lt;/ul>
&lt;p>You can install everything with a few clicks, no need to search for downloads or type commands.&lt;/p>
&lt;h2 id="step-8-enjoy-your-new-linux-setup">Step 8: Enjoy Your New Linux Setup&lt;/h2>
&lt;p>You&amp;rsquo;re done! You now have:&lt;/p>
&lt;ul>
&lt;li>A secure, stable operating system.&lt;/li>
&lt;li>Access to thousands of PC games via Steam and Heroic.&lt;/li>
&lt;li>Full productivity tools (LibreOffice and Chrome).&lt;/li>
&lt;li>No upgrade fees, no license keys, and full control over your system.&lt;/li>
&lt;/ul>
&lt;p>Linux today isn&amp;rsquo;t just for tech experts, it&amp;rsquo;s for anyone who wants speed, reliability, and freedom from Microsoft.&lt;/p></description></item><item><title>Why Event Driven Systems Are Not That Hard</title><link>https://www.darrenhorrocks.co.uk/why-event-driven-systems-are-not-that-hard/</link><pubDate>Tue, 16 Sep 2025 12:42:34 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-event-driven-systems-are-not-that-hard/</guid><description>&lt;p>Having recently read the article &amp;ldquo;&lt;a href="https://newsletter.scalablethread.com/p/why-event-driven-systems-are-hard">Why are Event Driven Systems Hard?&lt;/a>&amp;rdquo;, and having worked on several event driven systems myself, I began to wonder why people so often frame these systems as inherently difficult. It’s true that event driven and distributed systems &lt;strong>can&lt;/strong> be hard to work with&amp;hellip; especially if approached without the right mindset or tooling, but that doesn’t mean they &lt;strong>are&lt;/strong> hard by default.&lt;/p>
&lt;p>In fact, many of the “hard parts” that architects and developers worry about such as eventual consistency, debugging asynchronous flows and ensuring reliability, are well understood and largely solved with modern practices. With the dotnet ecosystem (or any other mature ecosystem), frameworks, and observability platforms, implementing event driven systems has become far more approachable than it was even a few years ago. The gap between theory and practice has narrowed significantly, making these architectures not only manageable but often the most straightforward option for scaling, decoupling, and evolving complex applications.&lt;/p>
&lt;p>I am going to focus on dotnet, because that is what I know&amp;hellip; Here we go:&lt;/p>
&lt;h2 id="publishing-and-handling-events-isnt-hard">&lt;strong>Publishing and Handling Events Isn’t Hard&lt;/strong>&lt;/h2>
&lt;p>In dotnet, you don’t need to reinvent the wheel. Frameworks like &lt;strong>MediatR&lt;/strong>, &lt;strong>MassTransit&lt;/strong>, and &lt;strong>NServiceBus&lt;/strong> make event publishing and consumption straightforward.&lt;/p>
&lt;p>&lt;strong>Example: Publishing a Domain Event with MediatR&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Define an event
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> record OrderPlacedEvent(Guid OrderId, &lt;span style="color:#66d9ef">string&lt;/span> CustomerEmail) : INotification;
&lt;span style="color:#75715e">// Publish event from domain service
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">OrderService&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> IMediator &lt;span style="color:#ae81ff">_&lt;/span>mediator;
&lt;span style="color:#66d9ef">public&lt;/span> OrderService(IMediator mediator)
{
&lt;span style="color:#ae81ff">_&lt;/span>mediator = mediator;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task PlaceOrder(Guid orderId, &lt;span style="color:#66d9ef">string&lt;/span> customerEmail)
{
&lt;span style="color:#75715e">// business logic...
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#ae81ff">_&lt;/span>mediator.Publish(&lt;span style="color:#66d9ef">new&lt;/span> OrderPlacedEvent(orderId, customerEmail));
}
}
&lt;span style="color:#75715e">// Event handler
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SendEmailOnOrderPlaced&lt;/span> : INotificationHandler&amp;lt;OrderPlacedEvent&amp;gt;
{
&lt;span style="color:#66d9ef">public&lt;/span> Task Handle(OrderPlacedEvent notification, CancellationToken cancellationToken)
{
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Email sent to {notification.CustomerEmail} for Order {notification.OrderId}&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> Task.CompletedTask;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This looks almost like in-process events, but can later evolve into distributed event handling with minimal refactoring.&lt;/p>
&lt;h2 id="distributed-messaging-made-easy-with-masstransit">&lt;strong>Distributed Messaging Made Easy with MassTransit&lt;/strong>&lt;/h2>
&lt;p>If you need &lt;strong>cross-service messaging&lt;/strong>, MassTransit (with RabbitMQ, Azure Service Bus, or Kafka) removes a lot of plumbing.&lt;/p>
&lt;p>&lt;strong>Publishing an Event (Producer)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">OrderController&lt;/span> : ControllerBase
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> IPublishEndpoint &lt;span style="color:#ae81ff">_&lt;/span>publishEndpoint;
&lt;span style="color:#66d9ef">public&lt;/span> OrderController(IPublishEndpoint publishEndpoint)
{
&lt;span style="color:#ae81ff">_&lt;/span>publishEndpoint = publishEndpoint;
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [HttpPost(&amp;#34;orders&amp;#34;)]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;IActionResult&amp;gt; CreateOrder(CreateOrderRequest request)
{
&lt;span style="color:#66d9ef">var&lt;/span> orderId = Guid.NewGuid();
&lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#ae81ff">_&lt;/span>publishEndpoint.Publish(&lt;span style="color:#66d9ef">new&lt;/span> OrderPlacedEvent(orderId, request.CustomerEmail));
&lt;span style="color:#66d9ef">return&lt;/span> Ok(orderId);
}
}
&lt;span style="color:#66d9ef">public&lt;/span> record OrderPlacedEvent(Guid OrderId, &lt;span style="color:#66d9ef">string&lt;/span> CustomerEmail);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Consuming an Event (Consumer)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">OrderPlacedConsumer&lt;/span> : IConsumer&amp;lt;OrderPlacedEvent&amp;gt;
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task Consume(ConsumeContext&amp;lt;OrderPlacedEvent&amp;gt; context)
{
&lt;span style="color:#66d9ef">var&lt;/span> evt = context.Message;
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;[Consumer] Sending welcome email to {evt.CustomerEmail}...&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">await&lt;/span> Task.CompletedTask;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>MassTransit wires up consumers, retries, error queues, and observability out of the box. You don’t have to build these yourself.&lt;/p>
&lt;h2 id="reliability-isnt-hard-use-the-outbox-pattern">&lt;strong>Reliability Isn’t Hard: Use the Outbox Pattern&lt;/strong>&lt;/h2>
&lt;p>A common complaint is &lt;em>“What if the event is lost between DB and message bus?”&lt;/em>
The &lt;strong>Outbox pattern&lt;/strong> ensures events are persisted with your data in the same transaction.&lt;/p>
&lt;p>&lt;strong>EF Core Outbox Example&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Entity with outbox entries
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">OutboxMessage&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> Guid Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Type { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Payload { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> DateTime OccurredOn { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> Processed { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;span style="color:#75715e">// Save event inside same transaction
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task PlaceOrder(DbContext db, Guid orderId, &lt;span style="color:#66d9ef">string&lt;/span> customerEmail)
{
&lt;span style="color:#66d9ef">var&lt;/span> order = &lt;span style="color:#66d9ef">new&lt;/span> Order { Id = orderId, CustomerEmail = customerEmail };
db.Orders.Add(order);
&lt;span style="color:#66d9ef">var&lt;/span> evt = &lt;span style="color:#66d9ef">new&lt;/span> OrderPlacedEvent(orderId, customerEmail);
&lt;span style="color:#66d9ef">var&lt;/span> outbox = &lt;span style="color:#66d9ef">new&lt;/span> OutboxMessage
{
Id = Guid.NewGuid(),
Type = evt.GetType().Name,
Payload = JsonSerializer.Serialize(evt),
OccurredOn = DateTime.UtcNow
};
db.Add(outbox);
&lt;span style="color:#66d9ef">await&lt;/span> db.SaveChangesAsync();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>A background service later reads &lt;strong>unprocessed OutboxMessages&lt;/strong>, publishes them to the bus, and marks them processed. Libraries like &lt;a href="https://github.com/oskardudycz/EventSourcing.NetCore/">EntityFrameworkCore.Outbox&lt;/a> already implement this.&lt;/p>
&lt;p>Reliability is solved with a proven pattern.&lt;/p>
&lt;h2 id="observability-isnt-hard-with-opentelemetry">&lt;strong>Observability Isn’t Hard with OpenTelemetry&lt;/strong>&lt;/h2>
&lt;p>Tracing async events can feel tricky, but &lt;strong>OpenTelemetry for dotnet&lt;/strong> integrates with MassTransit, Kafka, RabbitMQ, and Azure Service Bus.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =&amp;gt;
{
tracerProviderBuilder
.AddAspNetCoreInstrumentation()
.AddMassTransitInstrumentation()
.AddHttpClientInstrumentation()
.AddJaegerExporter(options =&amp;gt;
{
options.AgentHost = &lt;span style="color:#e6db74">&amp;#34;localhost&amp;#34;&lt;/span>;
options.AgentPort = &lt;span style="color:#ae81ff">6831&lt;/span>;
});
});
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Every published and consumed event now gets correlated spans in Jaeger/Zipkin/Grafana Tempo.&lt;/p>
&lt;h2 id="schema-contracts-arent-hard-with-asyncapi">&lt;strong>Schema Contracts Aren’t Hard with AsyncAPI&lt;/strong>&lt;/h2>
&lt;p>Instead of “wild west” event schemas, dotnet teams can adopt &lt;strong>AsyncAPI&lt;/strong> to describe event contracts, similar to Swagger for REST.&lt;/p>
&lt;p>&lt;strong>Example OrderPlacedEvent AsyncAPI snippet:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="color:#f92672">components&lt;/span>:
&lt;span style="color:#f92672">messages&lt;/span>:
&lt;span style="color:#f92672">OrderPlacedEvent&lt;/span>:
&lt;span style="color:#f92672">contentType&lt;/span>: &lt;span style="color:#ae81ff">application/json&lt;/span>
&lt;span style="color:#f92672">payload&lt;/span>:
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">object&lt;/span>
&lt;span style="color:#f92672">properties&lt;/span>:
&lt;span style="color:#f92672">orderId&lt;/span>:
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">string&lt;/span>
&lt;span style="color:#f92672">format&lt;/span>: &lt;span style="color:#ae81ff">uuid&lt;/span>
&lt;span style="color:#f92672">customerEmail&lt;/span>:
&lt;span style="color:#f92672">type&lt;/span>: &lt;span style="color:#ae81ff">string&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Tools like &lt;strong>NSwag + AsyncAPI generator&lt;/strong> can auto-generate C# contracts from this spec, removing guesswork.&lt;/p>
&lt;h2 id="debugging-isnt-hard-with-event-stores">&lt;strong>Debugging Isn’t Hard with Event Stores&lt;/strong>&lt;/h2>
&lt;p>If you want full &lt;strong>auditing and replayability&lt;/strong>, libraries like &lt;a href="https://www.eventstore.com/">EventStoreDB&lt;/a> integrate smoothly with dotnet.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Append to stream
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">await&lt;/span> eventStore.AppendToStreamAsync(
&lt;span style="color:#e6db74">$&amp;#34;order-{orderId}&amp;#34;&lt;/span>,
StreamState.Any,
&lt;span style="color:#66d9ef">new&lt;/span>[] { &lt;span style="color:#66d9ef">new&lt;/span> EventData(Uuid.NewUuid(), &lt;span style="color:#e6db74">&amp;#34;OrderPlaced&amp;#34;&lt;/span>, JsonSerializer.SerializeToUtf8Bytes(evt)) }
);
&lt;span style="color:#75715e">// Read back
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> events = eventStore.ReadStreamAsync(Direction.Forwards, &lt;span style="color:#e6db74">$&amp;#34;order-{orderId}&amp;#34;&lt;/span>, StreamPosition.Start);
&lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> resolvedEvent &lt;span style="color:#66d9ef">in&lt;/span> events)
{
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Event: {resolvedEvent.Event.EventType}&amp;#34;&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Debugging means you can replay from the event log, no mystery black box.&lt;/p>
&lt;h1 id="summary">Summary&lt;/h1>
&lt;p>Event driven systems &lt;strong>don’t have to be hard&lt;/strong> when you leverage the right patterns and libraries:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>MediatR&lt;/strong>: simple in-process events, evolves to distributed.&lt;/li>
&lt;li>&lt;strong>MassTransit / NServiceBus&lt;/strong>: cross-service pub/sub with retries, DLQs, observability baked in.&lt;/li>
&lt;li>&lt;strong>Outbox pattern&lt;/strong>: guarantees no event loss.&lt;/li>
&lt;li>&lt;strong>OpenTelemetry&lt;/strong>: distributed tracing across async boundaries.&lt;/li>
&lt;li>&lt;strong>AsyncAPI / NSwag&lt;/strong>: enforce clear event contracts.&lt;/li>
&lt;li>&lt;strong>EventStoreDB&lt;/strong>: full event sourcing + replay for debugging.&lt;/li>
&lt;/ul>
&lt;p>With these tools, most of the “hard” parts are already solved, so you can focus on domain modeling and business outcomes, not plumbing.&lt;/p></description></item><item><title>How to Upgrade Windows 10 to Windows 11 on Unsupported Hardware (Step-by-Step with Rufus)</title><link>https://www.darrenhorrocks.co.uk/how-to-upgrade-windows-10-to-windows-11-unsupported-hardware-stepbystep-with-rufus/</link><pubDate>Thu, 11 Sep 2025 13:25:45 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/how-to-upgrade-windows-10-to-windows-11-unsupported-hardware-stepbystep-with-rufus/</guid><description>&lt;p>If your computer doesn’t meet Microsoft’s system requirements for Windows 11, you may have seen the dreaded message: &lt;em>“This PC can’t run Windows 11.”&lt;/em> The usual culprits are missing &lt;strong>TPM 2.0&lt;/strong>, no &lt;strong>Secure Boot&lt;/strong>, or an older CPU.&lt;/p>
&lt;p>But here’s the good news: you can still &lt;strong>upgrade Windows 10 to Windows 11 on unsupported hardware&lt;/strong> using a free tool called &lt;strong>Rufus&lt;/strong>. This method avoids complicated registry edits and gives you a cleaner installation experience.&lt;/p>
&lt;p>In this guide, I’ll show you step by step how to install Windows 11 on unsupported PCs using Rufus.&lt;/p>
&lt;h2 id="before-you-start-important-note">Before You Start: Important Note&lt;/h2>
&lt;p>This method works well, but keep in mind:&lt;/p>
&lt;ul>
&lt;li>Microsoft does &lt;strong>not officially support Windows 11 on unsupported hardware&lt;/strong>.&lt;/li>
&lt;li>You may not receive future feature updates automatically.&lt;/li>
&lt;li>Security updates could be limited in the future.&lt;/li>
&lt;li>Always &lt;strong>back up your files&lt;/strong> before starting the upgrade.&lt;/li>
&lt;/ul>
&lt;h2 id="what-you-need-to-upgrade-to-windows-11-with-rufus">What You Need to Upgrade to Windows 11 with Rufus&lt;/h2>
&lt;p>To follow this tutorial, make sure you have:&lt;/p>
&lt;ul>
&lt;li>A USB drive with at least &lt;strong>8GB&lt;/strong> of storage&lt;/li>
&lt;li>The latest version of &lt;strong>Rufus&lt;/strong> (free download)&lt;/li>
&lt;li>The official &lt;strong>Windows 11 ISO file&lt;/strong> from Microsoft&lt;/li>
&lt;li>A PC currently running &lt;strong>Windows 10&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h2 id="step-1-download-rufus-and-the-windows-11-iso">Step 1: Download Rufus and the Windows 11 ISO&lt;/h2>
&lt;ol>
&lt;li>Go to the &lt;a href="https://rufus.ie/">Rufus website&lt;/a> and download the newest version.&lt;/li>
&lt;li>Visit the &lt;a href="https://www.microsoft.com/software-download/windows11">Microsoft Windows 11 download page&lt;/a>.&lt;/li>
&lt;li>Choose &lt;strong>Download Windows 11 Disk Image (ISO)&lt;/strong> and save the file.&lt;/li>
&lt;/ol>
&lt;h2 id="step-2-create-a-bootable-windows-11-usb-with-rufus">Step 2: Create a Bootable Windows 11 USB with Rufus&lt;/h2>
&lt;p>Now let’s prepare the installation media:&lt;/p>
&lt;ol>
&lt;li>Insert your USB drive into your computer.&lt;/li>
&lt;li>Open &lt;strong>Rufus&lt;/strong>.&lt;/li>
&lt;li>Under &lt;strong>Device&lt;/strong>, select your USB drive.&lt;/li>
&lt;li>Under &lt;strong>Boot selection&lt;/strong>, browse and select the Windows 11 ISO you downloaded.&lt;/li>
&lt;/ol>
&lt;p>When you choose the ISO, Rufus will display customization options that let you bypass Windows 11 requirements. These include:&lt;/p>
&lt;ul>
&lt;li>Remove requirement for &lt;strong>TPM 2.0&lt;/strong>&lt;/li>
&lt;li>Remove requirement for &lt;strong>Secure Boot&lt;/strong>&lt;/li>
&lt;li>Remove requirement for &lt;strong>RAM and CPU checks&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Tick the options that apply to your situation (or to be safe, just tick all three).&lt;/p>
&lt;p>Click &lt;strong>Start&lt;/strong>, and Rufus will create a bootable USB installer that bypasses all hardware checks.&lt;/p>
&lt;h2 id="step-3-upgrade-from-windows-10-to-windows-11">Step 3: Upgrade from Windows 10 to Windows 11&lt;/h2>
&lt;p>Once Rufus finishes:&lt;/p>
&lt;ol>
&lt;li>Open your new USB drive in &lt;strong>File Explorer&lt;/strong>.&lt;/li>
&lt;li>Run &lt;strong>setup.exe&lt;/strong>.&lt;/li>
&lt;li>Select &lt;strong>Keep personal files and apps&lt;/strong> if you want to upgrade without losing your data.&lt;/li>
&lt;li>Accept the warning about unsupported hardware.&lt;/li>
&lt;/ol>
&lt;p>Windows 11 will now install on your PC, even if it doesn’t meet Microsoft’s official system requirements.&lt;/p>
&lt;h2 id="step-4-enjoy-windows-11-on-unsupported-pcs">Step 4: Enjoy Windows 11 on Unsupported PCs&lt;/h2>
&lt;p>When your computer restarts, you’ll be greeted with Windows 11 running smoothly on your previously unsupported hardware.&lt;/p>
&lt;h3 id="a-few-things-to-remember">A few things to remember:&lt;/h3>
&lt;ul>
&lt;li>You should still receive &lt;strong>security updates&lt;/strong> from Microsoft.&lt;/li>
&lt;li>Major &lt;strong>feature updates&lt;/strong> may require repeating this process.&lt;/li>
&lt;li>Always keep a &lt;strong>backup&lt;/strong> of important files, just in case.&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Upgrading from &lt;strong>Windows 10 to Windows 11 on unsupported hardware with Rufus&lt;/strong> is one of the easiest and safest methods. It removes the need for registry hacks and gives you control over which requirements to skip.&lt;/p>
&lt;p>If you’ve been waiting to try Windows 11 but your PC isn’t supported, Rufus makes it possible in just a few clicks.&lt;/p></description></item><item><title>Why UUIDs Beat Integers as Primary Keys (And Why Performance Isn’t the Issue)</title><link>https://www.darrenhorrocks.co.uk/why-uuids-beat-integers-as-primary-keys-and-why-performance-isnt-issue/</link><pubDate>Mon, 08 Sep 2025 22:53:04 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-uuids-beat-integers-as-primary-keys-and-why-performance-isnt-issue/</guid><description>&lt;p>One of the first schema decisions you face when designing a database table is:
&lt;strong>Should I use an &lt;code>INT&lt;/code> or a &lt;code>UUID&lt;/code> as the primary key?&lt;/strong>&lt;/p>
&lt;p>Most developers default to an auto-incrementing integer. It’s simple, compact, and familiar. But UUIDs (a.k.a. GUIDs) are increasingly popular — and many of the old performance objections don’t hold up with modern hardware.&lt;/p>
&lt;p>Let’s break this down like developers, not DB theorists.&lt;/p>
&lt;h2 id="whats-a-uuid-really">What’s a UUID, Really?&lt;/h2>
&lt;p>At the end of the day, a UUID is just a number.
Specifically: a &lt;strong>128-bit number&lt;/strong>.&lt;/p>
&lt;p>Compare that to the types you already know:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Type&lt;/th>
&lt;th>Size (bits)&lt;/th>
&lt;th>Size (bytes)&lt;/th>
&lt;th>Max Values&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>INT&lt;/code>&lt;/td>
&lt;td>32&lt;/td>
&lt;td>4&lt;/td>
&lt;td>~4.3B&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>BIGINT&lt;/code>&lt;/td>
&lt;td>64&lt;/td>
&lt;td>8&lt;/td>
&lt;td>~9.2 quintillion&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>UUID&lt;/code>&lt;/td>
&lt;td>128&lt;/td>
&lt;td>16&lt;/td>
&lt;td>~3.4 × 10³⁸&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>That’s it. No magic. Just more space.&lt;/p>
&lt;h2 id="why-developers-like-uuids">Why Developers Like UUIDs&lt;/h2>
&lt;h3 id="1-generate-anywhere">1. Generate Anywhere&lt;/h3>
&lt;p>With integers, the database usually assigns the ID:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">TABLE&lt;/span> users (
id INT AUTO_INCREMENT &lt;span style="color:#66d9ef">PRIMARY&lt;/span> &lt;span style="color:#66d9ef">KEY&lt;/span>,
name VARCHAR(&lt;span style="color:#ae81ff">50&lt;/span>)
);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You don’t know the ID until after the row is inserted. With UUIDs, you can generate the ID in your application:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">TABLE&lt;/span> users (
id UUID &lt;span style="color:#66d9ef">PRIMARY&lt;/span> &lt;span style="color:#66d9ef">KEY&lt;/span>,
name VARCHAR(&lt;span style="color:#ae81ff">50&lt;/span>)
);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then in code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-python" data-lang="python">&lt;span style="color:#f92672">import&lt;/span> uuid
user_id &lt;span style="color:#f92672">=&lt;/span> uuid&lt;span style="color:#f92672">.&lt;/span>uuid4()
&lt;/code>&lt;/pre>&lt;/div>&lt;p>No database round trip just to get a key.&lt;/p>
&lt;h3 id="2-merge-without-collisions">2. Merge Without Collisions&lt;/h3>
&lt;p>If you’ve ever tried merging data from two databases that both used &lt;code>AUTO_INCREMENT&lt;/code>, you know the pain of ID collisions.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#75715e">-- From DB A
&lt;/span>&lt;span style="color:#75715e">&lt;/span>id &lt;span style="color:#f92672">|&lt;/span> name
&lt;span style="color:#75715e">---+-------
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#f92672">|&lt;/span> Alice
&lt;span style="color:#ae81ff">2&lt;/span> &lt;span style="color:#f92672">|&lt;/span> Bob
&lt;span style="color:#75715e">-- From DB B
&lt;/span>&lt;span style="color:#75715e">&lt;/span>id &lt;span style="color:#f92672">|&lt;/span> name
&lt;span style="color:#75715e">---+-------
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#f92672">|&lt;/span> Charlie
&lt;span style="color:#ae81ff">2&lt;/span> &lt;span style="color:#f92672">|&lt;/span> Dana
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Good luck merging those.&lt;/p>
&lt;p>With UUIDs:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">id &lt;span style="color:#f92672">|&lt;/span> name
&lt;span style="color:#75715e">-------------------------------------+-------
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#ae81ff">550&lt;/span>e8400&lt;span style="color:#f92672">-&lt;/span>e29b&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">41&lt;/span>d4&lt;span style="color:#f92672">-&lt;/span>a716&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">446655440000&lt;/span> &lt;span style="color:#f92672">|&lt;/span> Alice
ddeb27fb&lt;span style="color:#f92672">-&lt;/span>d9a0&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">4624&lt;/span>&lt;span style="color:#f92672">-&lt;/span>be4d&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">4615062&lt;/span>daed4 &lt;span style="color:#f92672">|&lt;/span> Bob
d8f1f8d4&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>b0f&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">11&lt;/span>ee&lt;span style="color:#f92672">-&lt;/span>be56&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">0242&lt;/span>ac120002 &lt;span style="color:#f92672">|&lt;/span> Charlie
&lt;span style="color:#ae81ff">42&lt;/span>c9aeea&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>b0f&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">11&lt;/span>ee&lt;span style="color:#f92672">-&lt;/span>be56&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">0242&lt;/span>ac120002 &lt;span style="color:#f92672">|&lt;/span> Dana
&lt;/code>&lt;/pre>&lt;/div>&lt;p>No collisions. Merge away.&lt;/p>
&lt;h3 id="3-safer-urls">3. Safer URLs&lt;/h3>
&lt;p>Ever seen a URL like &lt;code>/user/12345&lt;/code>?
Anyone can guess &lt;code>/user/12346&lt;/code>.&lt;/p>
&lt;p>UUIDs make this harder:&lt;/p>
&lt;pre tabindex="0">&lt;code>/user/550e8400-e29b-41d4-a716-446655440000
&lt;/code>&lt;/pre>&lt;p>Much less guessable.&lt;/p>
&lt;h2 id="the-performance-myth">The Performance Myth&lt;/h2>
&lt;p>Here’s the common objection:&lt;/p>
&lt;blockquote>
&lt;p>“But UUIDs are &lt;em>so much bigger&lt;/em> than integers. Won’t that kill performance?”&lt;/p>
&lt;/blockquote>
&lt;p>Not really.&lt;/p>
&lt;ul>
&lt;li>&lt;code>INT&lt;/code> = 4 bytes&lt;/li>
&lt;li>&lt;code>BIGINT&lt;/code> = 8 bytes&lt;/li>
&lt;li>&lt;code>UUID&lt;/code> = 16 bytes&lt;/li>
&lt;/ul>
&lt;p>That’s &lt;strong>12 extra bytes per row&lt;/strong> compared to an &lt;code>INT&lt;/code>. For perspective, a single &lt;code>VARCHAR(255)&lt;/code> column can take 255 bytes — orders of magnitude more than the difference between keys.&lt;/p>
&lt;p>Modern CPUs handle 128-bit comparisons natively. Databases already juggle kilobytes of row data per query. The cost of comparing two UUIDs instead of two integers is microscopic compared to disk I/O, joins, or network latency.&lt;/p>
&lt;p>&lt;strong>Real-world bottlenecks are almost never the 16-byte key.&lt;/strong>&lt;/p>
&lt;h2 id="things-to-watch-out-for">Things to Watch Out For&lt;/h2>
&lt;p>That said, UUIDs aren’t free candy:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Bigger indexes&lt;/strong> → Indexes on UUID columns take more space. If you’re storing billions of rows, that matters.&lt;/li>
&lt;li>&lt;strong>Fragmentation&lt;/strong> → Random UUIDs don’t insert sequentially. This can cause page splits in clustered indexes. Solutions: use &lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_%28date-time_and_MAC_address%29">UUIDv1&lt;/a> or database-specific functions like &lt;code>UUID_TO_BIN()&lt;/code> (MySQL) that produce more sequential values.&lt;/li>
&lt;li>&lt;strong>Readability&lt;/strong> → Debugging &lt;code>12345&lt;/code> is easier than &lt;code>550e8400-e29b-41d4-a716-446655440000&lt;/code>. Most teams solve this with tooling/log formatting.&lt;/li>
&lt;/ul>
&lt;h2 id="rule-of-thumb-for-developers">Rule of Thumb for Developers&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>Use &lt;strong>&lt;code>INT&lt;/code> or &lt;code>BIGINT&lt;/code>&lt;/strong> if:&lt;/p>
&lt;ul>
&lt;li>Your app is small and won’t need cross-database merges.&lt;/li>
&lt;li>You want compact storage and don’t care about guessable IDs.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Use &lt;strong>&lt;code>UUID&lt;/code>&lt;/strong> if:&lt;/p>
&lt;ul>
&lt;li>You’re building distributed or microservice systems.&lt;/li>
&lt;li>You need to merge data from multiple sources.&lt;/li>
&lt;li>You want to generate keys outside the database.&lt;/li>
&lt;li>You want harder-to-guess identifiers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="benchmark-int-vs-uuid-in-postgresql">Benchmark: &lt;code>INT&lt;/code> vs &lt;code>UUID&lt;/code> in PostgreSQL&lt;/h2>
&lt;p>Let’s test with two identical tables:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#75715e">-- INT-based table
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">TABLE&lt;/span> users_int (
id SERIAL &lt;span style="color:#66d9ef">PRIMARY&lt;/span> &lt;span style="color:#66d9ef">KEY&lt;/span>,
name TEXT
);
&lt;span style="color:#75715e">-- UUID-based table
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">TABLE&lt;/span> users_uuid (
id UUID &lt;span style="color:#66d9ef">PRIMARY&lt;/span> &lt;span style="color:#66d9ef">KEY&lt;/span> &lt;span style="color:#66d9ef">DEFAULT&lt;/span> gen_random_uuid(),
name TEXT
);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We’ll insert &lt;strong>1 million rows&lt;/strong> into each:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#75715e">-- Insert into INT table
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">INSERT&lt;/span> &lt;span style="color:#66d9ef">INTO&lt;/span> users_int (name)
&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#e6db74">&amp;#39;user_&amp;#39;&lt;/span> &lt;span style="color:#f92672">||&lt;/span> &lt;span style="color:#66d9ef">g&lt;/span>
&lt;span style="color:#66d9ef">FROM&lt;/span> generate_series(&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">1000000&lt;/span>) &lt;span style="color:#66d9ef">g&lt;/span>;
&lt;span style="color:#75715e">-- Insert into UUID table
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">INSERT&lt;/span> &lt;span style="color:#66d9ef">INTO&lt;/span> users_uuid (name)
&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#e6db74">&amp;#39;user_&amp;#39;&lt;/span> &lt;span style="color:#f92672">||&lt;/span> &lt;span style="color:#66d9ef">g&lt;/span>
&lt;span style="color:#66d9ef">FROM&lt;/span> generate_series(&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">1000000&lt;/span>) &lt;span style="color:#66d9ef">g&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="query-performance">Query Performance&lt;/h3>
&lt;p>&lt;strong>Point lookup by primary key&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">EXPLAIN&lt;/span> &lt;span style="color:#66d9ef">ANALYZE&lt;/span>
&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> users_int &lt;span style="color:#66d9ef">WHERE&lt;/span> id &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">500000&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output (abridged):&lt;/p>
&lt;pre tabindex="0">&lt;code>Index Scan using users_int_pkey on users_int
(cost=0.42..8.44 rows=1 width=36)
(actual time=0.018 ms)
&lt;/code>&lt;/pre>&lt;p>Now with UUID:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">EXPLAIN&lt;/span> &lt;span style="color:#66d9ef">ANALYZE&lt;/span>
&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> users_uuid
&lt;span style="color:#66d9ef">WHERE&lt;/span> id &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;550e8400-e29b-41d4-a716-446655440000&amp;#39;&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output (abridged):&lt;/p>
&lt;pre tabindex="0">&lt;code>Index Scan using users_uuid_pkey on users_uuid
(cost=0.42..8.44 rows=1 width=52)
(actual time=0.019 ms)
&lt;/code>&lt;/pre>&lt;p>&lt;strong>The difference? Basically zero.&lt;/strong> Both queries run in ~0.02ms.&lt;/p>
&lt;h3 id="index-size">Index Size&lt;/h3>
&lt;p>Let’s compare index sizes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span>
relname &lt;span style="color:#66d9ef">AS&lt;/span> index_name,
pg_size_pretty(pg_relation_size(indexrelid)) &lt;span style="color:#66d9ef">AS&lt;/span> index_size
&lt;span style="color:#66d9ef">FROM&lt;/span> pg_stat_all_indexes
&lt;span style="color:#66d9ef">WHERE&lt;/span> relname &lt;span style="color:#66d9ef">LIKE&lt;/span> &lt;span style="color:#e6db74">&amp;#39;users_%_pkey&amp;#39;&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Sample output:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>index_name&lt;/th>
&lt;th>index_size&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>users_int_pkey&lt;/td>
&lt;td>35 MB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>users_uuid_pkey&lt;/td>
&lt;td>70 MB&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Yes, the UUID index is bigger (about double, which makes sense: 16 bytes vs 8). But even at 1 million rows, we’re only talking tens of MB — trivial on modern systems.&lt;/p>
&lt;h3 id="takeaway-for-developers">Takeaway for Developers&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Lookup speed&lt;/strong>: Identical.&lt;/li>
&lt;li>&lt;strong>Insert speed&lt;/strong>: Nearly identical (UUIDs may fragment clustered indexes if totally random, but UUIDv1/sequential UUIDs solve this).&lt;/li>
&lt;li>&lt;strong>Storage&lt;/strong>: UUIDs take more index space, but unless you’re working with billions of rows, it’s not the bottleneck.&lt;/li>
&lt;/ul>
&lt;p>The actual performance bottlenecks will almost always be &lt;strong>query design, joins, and I/O&lt;/strong> — not whether your primary key is 4 bytes or 16.&lt;/p>
&lt;p>So when someone says &lt;em>“UUIDs are slow”&lt;/em>, you can point at query plans and actual numbers: they’re not.&lt;/p>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;ul>
&lt;li>A UUID is just a &lt;strong>128-bit number&lt;/strong>, the same way an &lt;code>INT&lt;/code> is a 32-bit number.&lt;/li>
&lt;li>The performance hit is negligible on modern hardware.&lt;/li>
&lt;li>UUIDs unlock distributed key generation, collision-free merges, and safer URLs.&lt;/li>
&lt;li>Integers are fine for simple, single-database apps — but UUIDs future-proof your schema.&lt;/li>
&lt;/ul></description></item><item><title>The Lie You Are Being Told About AI and Junior Developers</title><link>https://www.darrenhorrocks.co.uk/lie-you-are-being-told-about-ai-junior-developers/</link><pubDate>Tue, 19 Aug 2025 13:32:57 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/lie-you-are-being-told-about-ai-junior-developers/</guid><description>&lt;p>Let&amp;rsquo;s get one thing straight. If you&amp;rsquo;ve been listening to the so-called &amp;ldquo;tech futurists&amp;rdquo; and clueless CEOs, you&amp;rsquo;ve probably been fed the same line: &lt;strong>AI is going to replace every entry-level developer.&lt;/strong>&lt;/p>
&lt;p>It&amp;rsquo;s a hot take for a podcast, a great headline for a tech bro&amp;rsquo;s newsletter, and an even better way to get a company&amp;rsquo;s stock price to jump. But it&amp;rsquo;s also a complete, unadulterated lie. And it&amp;rsquo;s time to call it out for what it is.&lt;/p>
&lt;p>AI is not a replacement for junior developers. It&amp;rsquo;s not a silver bullet that eliminates the need for human talent. It&amp;rsquo;s a tool. It&amp;rsquo;s an assistant. Anyone who tells you otherwise either doesn&amp;rsquo;t understand software development or is trying to sell you something.&lt;/p>
&lt;h3 id="the-uncomfortable-truth-ai-needs-a-pilot">The Uncomfortable Truth: AI Needs a Pilot&lt;/h3>
&lt;p>The people who think AI will replace junior developers fundamentally misunderstand what coding is. They believe it&amp;rsquo;s just typing, like a human word processor. They see an AI spit out a block of code and think, &amp;ldquo;Great, problem solved. I don&amp;rsquo;t need that junior dev anymore.&amp;rdquo;&lt;/p>
&lt;p>This kind of thinking is dangerous and naive.&lt;/p>
&lt;p>Think of a junior developer as a pilot in training. The AI is the autopilot—incredibly sophisticated, capable of handling routine flight patterns, and great at making the journey smoother. But who&amp;rsquo;s in the cockpit? The pilot. Handing a complex project to an AI without a human in charge is like giving a toddler a chainsaw and telling them to go build a treehouse. You might get a result, but it&amp;rsquo;s going to be a disaster.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>You&amp;rsquo;re the guardrail.&lt;/strong> AI can&amp;rsquo;t check its own work. It can generate code that looks right, but you, the developer, are the one who has to verify it, test it, and ensure it doesn&amp;rsquo;t create a security nightmare. You&amp;rsquo;re the one with the critical thinking to see that the code is technically correct but completely wrong for the project&amp;rsquo;s long-term health.&lt;/li>
&lt;li>&lt;strong>You&amp;rsquo;re the shepherd.&lt;/strong> AI needs to be guided. It doesn&amp;rsquo;t understand context, business goals, or the nuanced &amp;ldquo;why&amp;rdquo; behind a project. Your job is to translate complex human intent into clear instructions for the AI, then take its output and shape it into a cohesive solution.&lt;/li>
&lt;li>&lt;strong>You&amp;rsquo;re the student.&lt;/strong> This is where the real value lies. AI is the greatest tutor you will ever have. Instead of being replaced by it, a smart junior developer will use it to accelerate their learning. Stuck on a complex problem? Ask the AI for a starting point. Unsure about a new framework? Have the AI walk you through a simple example.&lt;/li>
&lt;/ul>
&lt;p>Don&amp;rsquo;t let anyone tell you AI is the end of the line for new talent. It&amp;rsquo;s the beginning of a new chapter where human creativity and critical thinking become even more valuable. The developers who will thrive in the future aren&amp;rsquo;t the ones who can just type code; they&amp;rsquo;re the ones who can master the most powerful tool on the planet and use it to build something incredible.&lt;/p>
&lt;p>So the next time you hear someone say a robot is coming for your job, just smile. You know the truth. You&amp;rsquo;re not being replaced; you&amp;rsquo;re being handed a superpower.&lt;/p></description></item><item><title>The C# Mistake You're Probably Making with Memory</title><link>https://www.darrenhorrocks.co.uk/c-mistake-youre-probably-making-with-memory/</link><pubDate>Tue, 19 Aug 2025 13:22:21 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/c-mistake-youre-probably-making-with-memory/</guid><description>&lt;p>C#&amp;rsquo;s Garbage Collector is a powerful tool for automatic memory management, but it doesn&amp;rsquo;t solve every resource management problem. While it efficiently cleans up unreferenced managed objects, there are specific resources that require a more deterministic approach to ensure they are released promptly. This is where the &lt;strong>&lt;code>using&lt;/code> statement&lt;/strong> comes in.&lt;/p>
&lt;h3 id="the-role-of-the-garbage-collector">The Role of the Garbage Collector&lt;/h3>
&lt;p>The &lt;strong>Garbage Collector (GC)&lt;/strong> in C# is designed to reclaim memory from managed objects that are no longer being used by the application. It works in the background, periodically identifying and freeing up memory, which prevents memory leaks and simplifies development. . However, the GC&amp;rsquo;s timing is nondeterministic—you can&amp;rsquo;t predict exactly when it will run.&lt;/p>
&lt;p>This nondeterministic nature is perfectly fine for memory, but it&amp;rsquo;s a problem for unmanaged resources like file handles, database connections, network sockets, or graphics handles. If these resources aren&amp;rsquo;t released as soon as they&amp;rsquo;re no longer needed, they can remain locked or open, causing issues like file access errors or resource exhaustion.&lt;/p>
&lt;h3 id="the-power-of-the-using-statement">The Power of the &lt;code>using&lt;/code> Statement&lt;/h3>
&lt;p>The &lt;strong>&lt;code>using&lt;/code> statement&lt;/strong> provides a simple and reliable way to ensure that these unmanaged resources are released promptly. It&amp;rsquo;s a syntactic shortcut for a &lt;code>try...finally&lt;/code> block that automatically calls the &lt;strong>&lt;code>Dispose()&lt;/code>&lt;/strong> method on an object when it goes out of scope.&lt;/p>
&lt;p>An object can be used in a &lt;code>using&lt;/code> statement only if it implements the &lt;strong>&lt;code>IDisposable&lt;/code>&lt;/strong> interface. This interface contains a single method, &lt;strong>&lt;code>Dispose()&lt;/code>&lt;/strong>, which is responsible for cleaning up the unmanaged resources held by the object.&lt;/p>
&lt;p>Here&amp;rsquo;s a quick example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Without a using statement
&lt;/span>&lt;span style="color:#75715e">&lt;/span>FileStream fs = &lt;span style="color:#66d9ef">null&lt;/span>;
&lt;span style="color:#66d9ef">try&lt;/span>
{
fs = &lt;span style="color:#66d9ef">new&lt;/span> FileStream(&lt;span style="color:#e6db74">&amp;#34;example.txt&amp;#34;&lt;/span>, FileMode.Open);
&lt;span style="color:#75715e">// Do something with the file
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">finally&lt;/span>
{
&lt;span style="color:#66d9ef">if&lt;/span> (fs != &lt;span style="color:#66d9ef">null&lt;/span>)
{
fs.Dispose();
}
}
&lt;span style="color:#75715e">// With a using statement
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> fs = &lt;span style="color:#66d9ef">new&lt;/span> FileStream(&lt;span style="color:#e6db74">&amp;#34;example.txt&amp;#34;&lt;/span>, FileMode.Open))
{
&lt;span style="color:#75715e">// Do something with the file
&lt;/span>&lt;span style="color:#75715e">&lt;/span>} &lt;span style="color:#75715e">// The Dispose() method is automatically called here
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, the &lt;code>using&lt;/code> statement makes the code cleaner and less prone to errors. It guarantees that the &lt;code>Dispose()&lt;/code> method is called even if an exception occurs within the block, ensuring resources are always released.&lt;/p>
&lt;h3 id="best-practices-for-resource-management">Best Practices for Resource Management&lt;/h3>
&lt;p>Incorporating the &lt;strong>&lt;code>using&lt;/code> statement&lt;/strong> into your daily coding habits is a simple but critical best practice. It ensures that your applications are robust, efficient, and well-behaved, especially when dealing with external resources.&lt;/p>
&lt;p>By using it, you&amp;rsquo;re not fighting against the Garbage Collector but instead working with it. The GC handles the memory, and you take deterministic control of valuable unmanaged resources. This combined approach leads to more stable and performant C# applications. So, next time you&amp;rsquo;re working with a file, a database connection, or any other &lt;code>IDisposable&lt;/code> object, remember to use a &lt;code>using&lt;/code> statement. It&amp;rsquo;s a small change that makes a big difference.&lt;/p></description></item><item><title>Don't Buffer, Stream! How IAsyncEnumerable&lt;T> Solves API Performance Issues</title><link>https://www.darrenhorrocks.co.uk/dont-buffer-stream-how-iasyncenumerablet-solves-api-performance-issues/</link><pubDate>Mon, 18 Aug 2025 11:34:34 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/dont-buffer-stream-how-iasyncenumerablet-solves-api-performance-issues/</guid><description>&lt;p>Building APIs that handle large datasets can be a challenge. A common approach is to collect all the data into a list, convert it to JSON, and then send it all at once. But what happens when that dataset is massive? Your API might freeze up while it&amp;rsquo;s building the response, and you could end up with a huge memory footprint. Fortunately, ASP.NET Core provides a great solution for this problem: &lt;strong>&lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code>&lt;/strong>.&lt;/p>
&lt;h3 id="what-is-iasyncenumerablet">What is &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code>?&lt;/h3>
&lt;p>Introduced in C# 8.0, &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code> allows you to iterate through data asynchronously. This is perfect for streaming scenarios where you need to process or return data in chunks, rather than all at once. When you use it in an ASP.NET Core API endpoint, the framework will serialise and send data to the client as it becomes available. This is different from the traditional approach, where the entire collection must be ready before the response can be sent.&lt;/p>
&lt;hr>
&lt;h3 id="why-use-iasyncenumerablet">Why Use &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code>?&lt;/h3>
&lt;p>There are two main benefits to using &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code> for your API endpoints:&lt;/p>
&lt;p>&lt;strong>1. Improved Performance:&lt;/strong> You no longer have to wait for the entire dataset to be built in memory. As soon as the first item is ready, it can be sent to the client. This can significantly reduce the time to first byte and make your API feel more responsive, especially for large queries.&lt;/p>
&lt;p>&lt;strong>2. Reduced Memory Usage:&lt;/strong> Instead of holding the entire collection in memory, your application only needs to hold a small number of items at a time. This is a game-changer for handling large datasets, as it can prevent your application from consuming excessive memory and potentially crashing.&lt;/p>
&lt;hr>
&lt;h3 id="how-to-implement-it">How to Implement It&lt;/h3>
&lt;p>Implementing &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code> in an ASP.NET Core API is straightforward. Here’s a simple example of a controller method that streams a large number of items.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#a6e22e">[ApiController]&lt;/span>
&lt;span style="color:#a6e22e">[Route(&amp;#34;[controller]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;)]
&lt;/span>&lt;span style="color:#e6db74">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ProductsController&lt;/span> : ControllerBase
{
&lt;span style="color:#75715e">// Simulates fetching a large number of products from a database
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> IAsyncEnumerable&amp;lt;Product&amp;gt; GetProductsFromDatabase()
{
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">1&lt;/span>; i &amp;lt;= &lt;span style="color:#ae81ff">10000&lt;/span>; i++)
{
&lt;span style="color:#66d9ef">await&lt;/span> Task.Delay(&lt;span style="color:#ae81ff">50&lt;/span>); &lt;span style="color:#75715e">// Simulate an async operation like a database call
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">yield&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> Product { Id = i, Name = &lt;span style="color:#e6db74">$&amp;#34;Product {i}&amp;#34;&lt;/span> };
}
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [HttpGet(&amp;#34;stream&amp;#34;)]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> IAsyncEnumerable&amp;lt;Product&amp;gt; GetStreamedProducts()
{
&lt;span style="color:#66d9ef">return&lt;/span> GetProductsFromDatabase();
}
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Product&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, the &lt;code>GetProductsFromDatabase&lt;/code> method uses the &lt;strong>&lt;code>yield return&lt;/code>&lt;/strong> keyword to return items one by one. The &lt;code>IAsyncEnumerable&amp;lt;Product&amp;gt;&lt;/code> return type tells the ASP.NET Core runtime to stream the data as it’s being generated.&lt;/p>
&lt;hr>
&lt;h3 id="important-considerations">Important Considerations&lt;/h3>
&lt;p>While &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code> is a powerful tool, it&amp;rsquo;s not a silver bullet. Keep these things in mind:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Client Compatibility:&lt;/strong> The client calling the API must be able to handle a streamed response. Most modern web clients and frameworks support this out of the box, but older or custom clients might have issues.&lt;/li>
&lt;li>&lt;strong>Error Handling:&lt;/strong> If an exception occurs partway through streaming, the client will receive an incomplete response. You need to handle errors gracefully and ensure the client can deal with a connection that might be abruptly closed.&lt;/li>
&lt;li>&lt;strong>HTTP Protocol:&lt;/strong> Under the hood, this works by leveraging the &lt;strong>Transfer-Encoding: chunked&lt;/strong> header in HTTP. This is standard for HTTP/1.1 and later, but it&amp;rsquo;s good to be aware of how the data is being sent.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>For backend developers working with large datasets, &lt;code>IAsyncEnumerable&amp;lt;T&amp;gt;&lt;/code> is a fantastic tool to have in your arsenal. It provides a simple and effective way to build more responsive and memory-efficient APIs. By streaming data instead of buffering it all in memory, you can significantly improve the user experience and the overall health of your application.&lt;/p></description></item><item><title>Most Unit Tests Are a Waste of Time, but You Need to Write Them Anyway</title><link>https://www.darrenhorrocks.co.uk/most-unit-tests-are-waste-time-but-you-need-to-write-them-anyway/</link><pubDate>Sat, 26 Jul 2025 20:03:53 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/most-unit-tests-are-waste-time-but-you-need-to-write-them-anyway/</guid><description>&lt;p>Yes, I&amp;rsquo;m saying it, &lt;strong>most unit tests feel like a waste of time&lt;/strong>.&lt;/p>
&lt;p>You write loads of them. They break when you refactor. They rarely catch real bugs. When you change implementations, you delete the tests. And when your build fails, it’s usually because a mocked method didn’t behave as expected — not because your code actually broke.&lt;/p>
&lt;p>So why do we keep writing them?&lt;/p>
&lt;p>Because while &lt;em>most&lt;/em> unit tests &lt;strong>are&lt;/strong> a waste of time, &lt;strong>some&lt;/strong> aren’t — and knowing the difference is what separates a codebase held together by guesswork from one that’s confidently shippable.&lt;/p>
&lt;h2 id="why-most-unit-tests-feel-pointless">Why Most Unit Tests Feel Pointless&lt;/h2>
&lt;p>In a typical C# MVC application, unit testing often devolves into &lt;strong>testing the mocks, not the code&lt;/strong>. Consider:&lt;/p>
&lt;h3 id="example">Example:&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> mockService = &lt;span style="color:#66d9ef">new&lt;/span> Mock&amp;lt;IUserService&amp;gt;();
mockService.Setup(x =&amp;gt; x.GetUser(&lt;span style="color:#ae81ff">1&lt;/span>)).Returns(&lt;span style="color:#66d9ef">new&lt;/span> User { Id = &lt;span style="color:#ae81ff">1&lt;/span> });
&lt;span style="color:#66d9ef">var&lt;/span> controller = &lt;span style="color:#66d9ef">new&lt;/span> UserController(mockService.Object);
&lt;span style="color:#66d9ef">var&lt;/span> result = controller.Get(&lt;span style="color:#ae81ff">1&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>What are we really testing here?
Not the routing. Not the service logic. Just that a mock returns a hardcoded result we told it to. If this breaks, the app doesn’t necessarily break. If this passes, it doesn’t mean the app works.&lt;/p>
&lt;p>Then there are &lt;strong>tests for trivial logic&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Add_ShouldReturnSum()
{
&lt;span style="color:#66d9ef">var&lt;/span> math = &lt;span style="color:#66d9ef">new&lt;/span> MathHelper();
&lt;span style="color:#66d9ef">var&lt;/span> result = math.Add(&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>);
Assert.AreEqual(&lt;span style="color:#ae81ff">4&lt;/span>, result);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Congratulations, you proved addition works.
Now imagine 300 of these tests — offering zero protection against real bugs.&lt;/p>
&lt;p>So yes, &lt;strong>most of these are a waste of your time&lt;/strong>.&lt;/p>
&lt;h2 id="but-heres-why-you-still-need-them">But Here&amp;rsquo;s Why You Still Need Them&lt;/h2>
&lt;p>Despite all that — you need to write tests anyway.&lt;/p>
&lt;p>Not &lt;em>because&lt;/em> they guarantee zero bugs (they don’t). But because:&lt;/p>
&lt;h3 id="tests-define-behavior">Tests Define Behavior&lt;/h3>
&lt;p>Well-written unit tests act as &lt;strong>executable documentation&lt;/strong>. They describe what the system is supposed to do, especially when future devs (or future you) forget the intent.&lt;/p>
&lt;h3 id="tests-enable-refactoring">Tests Enable Refactoring&lt;/h3>
&lt;p>You can’t safely refactor without some safety net. If you’re pulling apart a C# service or controller, tests that cover logic (not mocks) help you know what broke &lt;em>immediately&lt;/em>.&lt;/p>
&lt;h3 id="tests-catch-edge-cases-youll-forget">Tests Catch Edge Cases You’ll Forget&lt;/h3>
&lt;p>You might test the happy path manually. But do you check null inputs every time? Timezone logic? Unauthorised users? Unit tests &lt;em>can&lt;/em> force you to handle those cases.&lt;/p>
&lt;h2 id="what-to-write-instead-of-meaningless-tests">What to Write Instead of Meaningless Tests&lt;/h2>
&lt;p>Since we’ve established that &lt;strong>not all tests are worth writing&lt;/strong>, here’s what &lt;em>is&lt;/em>:&lt;/p>
&lt;h3 id="1-test-real-business-logic">1. &lt;strong>Test Real Business Logic&lt;/strong>&lt;/h3>
&lt;p>Skip trivial logic. Instead, focus on &lt;strong>functions that make decisions&lt;/strong>, mutate data, or contain rules.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> PromoteUser_ShouldSetRoleToAdmin()
{
&lt;span style="color:#66d9ef">var&lt;/span> user = &lt;span style="color:#66d9ef">new&lt;/span> User(&lt;span style="color:#e6db74">&amp;#34;basic&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> service = &lt;span style="color:#66d9ef">new&lt;/span> UserService();
service.Promote(user);
Assert.AreEqual(&lt;span style="color:#e6db74">&amp;#34;Admin&amp;#34;&lt;/span>, user.Role);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is logic that could break. This test is worth writing.&lt;/p>
&lt;h3 id="2-use-in-memory-dependencies-instead-of-mocks">2. &lt;strong>Use In-Memory Dependencies Instead of Mocks&lt;/strong>&lt;/h3>
&lt;p>Whenever possible, avoid mocking and use real implementations or in-memory versions.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> options = &lt;span style="color:#66d9ef">new&lt;/span> DbContextOptionsBuilder&amp;lt;AppDbContext&amp;gt;()
.UseInMemoryDatabase(&lt;span style="color:#e6db74">&amp;#34;TestDb&amp;#34;&lt;/span>)
.Options;
&lt;span style="color:#66d9ef">using&lt;/span> var context = &lt;span style="color:#66d9ef">new&lt;/span> AppDbContext(options);
context.Users.Add(&lt;span style="color:#66d9ef">new&lt;/span> User { Id = &lt;span style="color:#ae81ff">1&lt;/span>, Name = &lt;span style="color:#e6db74">&amp;#34;Alice&amp;#34;&lt;/span> });
context.SaveChanges();
&lt;span style="color:#66d9ef">var&lt;/span> service = &lt;span style="color:#66d9ef">new&lt;/span> UserService(context);
&lt;span style="color:#66d9ef">var&lt;/span> user = service.GetUser(&lt;span style="color:#ae81ff">1&lt;/span>);
Assert.AreEqual(&lt;span style="color:#e6db74">&amp;#34;Alice&amp;#34;&lt;/span>, user.Name);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This approach:&lt;/p>
&lt;ul>
&lt;li>Covers real database logic (e.g., query filters, relationships)&lt;/li>
&lt;li>Doesn’t break when you rename a method&lt;/li>
&lt;li>Actually catches bugs&lt;/li>
&lt;/ul>
&lt;h3 id="3-favor-integration-tests-for-controllers">3. &lt;strong>Favor Integration Tests for Controllers&lt;/strong>&lt;/h3>
&lt;p>In C# MVC apps, controller unit tests with mocked services rarely give you confidence. Instead, use &lt;strong>integration tests&lt;/strong> that spin up the full pipeline:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task GetUser_ReturnsExpectedUser()
{
&lt;span style="color:#66d9ef">var&lt;/span> client = &lt;span style="color:#ae81ff">_f&lt;/span>actory.CreateClient();
&lt;span style="color:#66d9ef">var&lt;/span> response = &lt;span style="color:#66d9ef">await&lt;/span> client.GetAsync(&lt;span style="color:#e6db74">&amp;#34;/api/user/1&amp;#34;&lt;/span>);
response.EnsureSuccessStatusCode();
&lt;span style="color:#66d9ef">var&lt;/span> body = &lt;span style="color:#66d9ef">await&lt;/span> response.Content.ReadAsStringAsync();
Assert.IsTrue(body.Contains(&lt;span style="color:#e6db74">&amp;#34;Alice&amp;#34;&lt;/span>));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This tells you:&lt;/p>
&lt;ul>
&lt;li>Routing works&lt;/li>
&lt;li>The controller is wired correctly&lt;/li>
&lt;li>The real service/database returned something valid&lt;/li>
&lt;/ul>
&lt;h2 id="so-whats-the-right-balance">So What’s the Right Balance?&lt;/h2>
&lt;p>Here&amp;rsquo;s a rule of thumb I’ve come to rely on:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>If a test would still pass after you’ve broken the app, delete it.&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>The tests worth keeping:&lt;/p>
&lt;ul>
&lt;li>Validate decisions or calculations&lt;/li>
&lt;li>Catch regressions during refactors&lt;/li>
&lt;li>Fail only when something meaningful breaks&lt;/li>
&lt;/ul>
&lt;h2 id="final-thoughts-write-less-test-smarter">Final Thoughts: Write Less, Test Smarter&lt;/h2>
&lt;p>You don’t need 100% test coverage. You need &lt;strong>meaningful coverage&lt;/strong>.&lt;/p>
&lt;p>So yes — &lt;strong>most unit tests are a waste of time&lt;/strong> when they’re written to check boxes or appease CI gates.
But tests that clarify logic, guard complex behavior, and give you confidence to ship? Those are worth every second.&lt;/p>
&lt;p>&lt;strong>Write tests like your future self depends on them — because they do.&lt;/strong>&lt;/p></description></item><item><title>Snap Unsnapped: Why Ubuntu's Packaging Isn't the Universal Dream You Were Promised</title><link>https://www.darrenhorrocks.co.uk/snap-unsnapped-why-ubuntus-packaging-isnt-universal-dream-you-were-promised/</link><pubDate>Tue, 22 Jul 2025 21:38:31 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/snap-unsnapped-why-ubuntus-packaging-isnt-universal-dream-you-were-promised/</guid><description>&lt;p>Ubuntu&amp;rsquo;s Snap packaging system was introduced with the promise of universal Linux applications, easy updates, and robust security through confinement. While these goals are admirable, the reality of Snap for many desktop users has been a source of frustration, leading to a growing sentiment that &amp;ldquo;Ubuntu Snap sucks.&amp;rdquo; This isn&amp;rsquo;t just a matter of preference; it&amp;rsquo;s rooted in several fundamental design choices that hobble user experience, resource efficiency, and even security.&lt;/p>
&lt;h3 id="the-walled-garden-isolation-to-a-fault">The Walled Garden: Isolation to a Fault&lt;/h3>
&lt;p>One of the most significant criticisms leveled against Snap is its &amp;ldquo;walled garden&amp;rdquo; approach. While touted as a security feature, the strict confinement of Snap applications often goes too far, isolating them from the very operating system features that make Linux a joy to use.&lt;/p>
&lt;p>Imagine trying to work with a program that can&amp;rsquo;t easily interact with the rest of your system. This is often the case with Snaps. Simple actions like &lt;strong>drag-and-dropping files&lt;/strong> from your file manager into a Snap application can be a frustrating exercise in futility. The application, confined within its &amp;ldquo;garden,&amp;rdquo; often lacks the necessary permissions to directly access files outside its designated sandboxed directories. Similarly, opening files from arbitrary locations on your system, or even saving to them, can become a convoluted process, breaking the seamless workflow expected of a modern desktop environment. This isolation, while aiming for security, inadvertently creates an inefficient and disjointed user experience.&lt;/p>
&lt;h3 id="resource-hog-high-ram-and-disk-space-usage">Resource Hog: High RAM and Disk Space Usage&lt;/h3>
&lt;p>Another common complaint revolves around Snap&amp;rsquo;s often-inflated resource consumption. Snaps bundle all their dependencies, even those already present on your system through traditional package managers. This &amp;ldquo;self-contained&amp;rdquo; nature, while simplifying deployment for developers, leads to significant redundancy for users.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>High Disk Space Usage:&lt;/strong> Each Snap application carries its own set of libraries and dependencies. If you have multiple Snaps that rely on the same underlying libraries (and many do, especially core GNOME components), those libraries are duplicated for each Snap. This quickly leads to a substantial increase in disk space usage compared to traditionally packaged applications that leverage system-wide shared libraries. Furthermore, Snap retains old versions of applications for rollback purposes, further contributing to disk bloat if not actively managed.&lt;/li>
&lt;li>&lt;strong>High RAM Usage:&lt;/strong> The duplicated libraries aren&amp;rsquo;t just a disk space concern; they can also impact RAM. When multiple Snap applications are running, they might each be loading their own copies of shared libraries into memory, rather than sharing a single instance. This can lead to a noticeable increase in RAM consumption, particularly on systems with limited memory, making the overall system feel sluggish.&lt;/li>
&lt;/ul>
&lt;h3 id="outdated-and-insecure-version-disparity-and-security-risks">Outdated and Insecure: Version Disparity and Security Risks&lt;/h3>
&lt;p>While Snaps are designed to offer quick updates, the reality can be quite different. A concerning issue for many users is the &lt;strong>version disparity between Snap packages and system packages&lt;/strong>. It&amp;rsquo;s not uncommon for the Snap version of a popular application to lag several versions behind what&amp;rsquo;s available through a distribution&amp;rsquo;s traditional package repositories (like APT on Ubuntu).&lt;/p>
&lt;p>This lag can be problematic for several reasons:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Missing Features and Bug Fixes:&lt;/strong> Users might miss out on the latest features, performance improvements, and crucial bug fixes present in newer versions.&lt;/li>
&lt;li>&lt;strong>Potential Security Vectors:&lt;/strong> Perhaps most critically, outdated software versions are often unpatched versions. When a Snap lags behind, it might not include recent security patches for vulnerabilities discovered in its bundled dependencies. This transforms the &amp;ldquo;security through confinement&amp;rdquo; promise into a potential security liability, as an unpatched vulnerability within a Snap could still be exploited, even if the rest of your system is up to date.&lt;/li>
&lt;/ul>
&lt;h3 id="the-ecosystem-conundrum-canonicals-control">The Ecosystem Conundrum: Canonical&amp;rsquo;s Control&lt;/h3>
&lt;p>Beyond the technical issues, the very nature of Snap as a Canonical-controlled ecosystem raises concerns for some in the open-source community. The Snap Store, the centralized repository for Snap applications, is proprietary. This level of control, while offering certain benefits in terms of curation and distribution for Canonical, goes against the decentralized and open philosophy that underpins much of the Linux world. This can lead to a feeling of being locked into a single vendor&amp;rsquo;s ecosystem, similar to the &amp;ldquo;walled gardens&amp;rdquo; seen in proprietary operating systems.&lt;/p>
&lt;h3 id="conclusion-a-promising-idea-with-implementation-flaws">Conclusion: A Promising Idea with Implementation Flaws&lt;/h3>
&lt;p>Ubuntu&amp;rsquo;s Snap technology, with its ambitions for universal packaging and enhanced security, had a strong vision. However, its current implementation on the desktop has introduced a range of issues that actively detract from the user experience. The restrictive confinement, leading to difficulties with fundamental desktop interactions like drag-and-drop, coupled with its resource-intensive nature and the often-outdated software versions, paints a picture of a system that, for many, &amp;ldquo;sucks.&amp;rdquo;&lt;/p>
&lt;p>While development continues and improvements are undoubtedly being made, the fundamental trade-offs in Snap&amp;rsquo;s design remain a significant hurdle for widespread acceptance and a source of ongoing frustration for a substantial portion of the Ubuntu user base. For those seeking a truly integrated, efficient, and open Linux desktop experience, the current state of Snap leaves much to be desired.&lt;/p></description></item><item><title>Why Leetcode Style Interview Tests Are Bullshit</title><link>https://www.darrenhorrocks.co.uk/why-leetcode-style-interview-tests-are-bullshit/</link><pubDate>Mon, 09 Jun 2025 09:24:04 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-leetcode-style-interview-tests-are-bullshit/</guid><description>&lt;p>Everybody has heard of the leetcode style interview tests, and given their prominence in the tech news recently, you won&amp;rsquo;t be surprised to find out that this is another story highlighting their inherent flaws.&lt;/p>
&lt;p>Recently, after a remote interview on teams, I was given a set of 3 different leetcode style interview test questions. I was told to take &amp;ldquo;no more than 3 hours total&amp;rdquo; and to &amp;ldquo;write it using typescrypt&amp;rdquo;.&lt;/p>
&lt;p>39 minutes later, I had completed and submitted all 3 tests, and all 3 tests pass all test cases. So now you ask, what&amp;rsquo;s the problem?&lt;/p>
&lt;h3 id="you-clearly-cheated-i-didnt">You Clearly Cheated?! (I didn&amp;rsquo;t)&lt;/h3>
&lt;p>I was immediately accused of cheating by the VP of Engineering, even though the site has monitoring tools to detect if i was cheating (which if i WAS cheating, which i wasn&amp;rsquo;t, it wouldn&amp;rsquo;t have detected anything). The site claims it would detect if I tabbed away, or if I had copied any text out of the test, or pasted anything into the test, I had my doubts, but I didnt need to cheat so why would I.&lt;/p>
&lt;p>Apparently, it is impossible, with 20+ years of experience being a professional software developer, to do what their VP of Engineering says should have taken 3 hours, in 39 minutes.&lt;/p>
&lt;p>They could provide no proof, the site and its &amp;ldquo;monitoring tools&amp;rdquo; detected no selecting of any of the text, no copying of the text, no pasting of anything at all, didn&amp;rsquo;t detect me tabbing away from the browser. The accusation was entirely down to, others who have taken it, have taken a lot longer, and the VP himself, took a lot longer.&lt;/p>
&lt;p>If you and your company put so much weight on these tests, you should:&lt;/p>
&lt;ol>
&lt;li>Be good at them&lt;/li>
&lt;li>Accept that there are some people, for whom it is possible to be better than you at them&lt;/li>
&lt;li>Don&amp;rsquo;t accuse people of cheating if you cannot show any evidence that they cheated&lt;/li>
&lt;/ol>
&lt;p>In the end, the interviewer (the VP of Engineering) would not back down, they would not accept that I didn&amp;rsquo;t use an LLM (such as copilot). I offered to do another test with them watching and also offered to show them that IF I was to be cheating, that it would not have taken me 39 minutes, they declined. After the call, I did go back and re-do the same 3 tests with the assistance of gemini, not copilot, because i wanted to see if it was possible to cheat with a tool that more people have access to, it took 4 minutes to complete all 3 tests.&lt;/p>
&lt;p>For anybody who is interested in trying the tests they gave me for themselves, these are the equivalent tests on leetcode itself (the site they had me use was a different site that requires a paid subscription by an employer):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://leetcode.com/problems/strong-password-checker-ii/description/">Strong Password Checker II&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://leetcode.com/problems/substring-matching-pattern/">Substring Matching Pattern&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://leetcode.com/problems/valid-sudoku">Valid Sudoku&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>How quickly can you get through these in typescript, or any other language, with and without cheating?&lt;/p></description></item><item><title>The Darker Side of Tech Layoffs (it's darker than it seems)</title><link>https://www.darrenhorrocks.co.uk/darker-side-tech-layoffs-its-darker-than-it-seems/</link><pubDate>Wed, 14 May 2025 12:56:37 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/darker-side-tech-layoffs-its-darker-than-it-seems/</guid><description>&lt;p>The headlines have been stark: tech giant after tech giant announcing significant layoffs. While the immediate impact on those made redundant is undeniable and deeply upsetting, the fallout from these decisions extends far beyond those who receive the dreaded news. The industry wide repercussions are creating a climate of increased pressure, stifled career movement, and potentially lower earning potential for a vast number of tech professionals.&lt;/p>
&lt;p>Let&amp;rsquo;s unpack the less visible, yet equally significant, ways tech redundancies are impacting everyone else.&lt;/p>
&lt;h3 id="the-weight-of-more-with-less-the-burden-on-remaining-employees">The Weight of More with Less: The Burden on Remaining Employees&lt;/h3>
&lt;p>For those who remain after a round of layoffs, a sense of relief can quickly morph into a feeling of overwhelming pressure. Companies, often driven by a desire to maintain output with a leaner workforce, expect the same amount of work &lt;em>&lt;strong>or even more&lt;/strong>&lt;/em> to be accomplished by fewer people, which can translate to:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Increased workload:&lt;/strong> Remaining employees are often expected to absorb the responsibilities of their former colleagues, leading to longer hours and a heavier burden.&lt;/li>
&lt;li>&lt;strong>Reduced resources:&lt;/strong> Teams may find themselves with fewer resources, tools, or support staff, making it harder to execute projects efficiently.&lt;/li>
&lt;li>&lt;strong>Heightened stress and anxiety:&lt;/strong> The fear of future layoffs, coupled with the increased workload, creates a climate of uncertainty and anxiety, impacting morale and well-being.&lt;/li>
&lt;li>&lt;strong>Skill gaps and the pressure to upskill rapidly:&lt;/strong> Individuals may be asked to take on tasks outside their core expertise, leading to pressure to learn new skills quickly without adequate support or training.&lt;/li>
&lt;/ul>
&lt;p>This environment inevitably fuels burnout. The tech industry was already grappling with high rates of stress and exhaustion. Asking fewer people to do the work of many is a surefire way to exacerbate this existing crisis.&lt;/p>
&lt;h3 id="the-golden-handcuffs-tighten-the-frustration-of-those-seeking-new-opportunities">The Golden Handcuffs Tighten: The Frustration of Those Seeking New Opportunities&lt;/h3>
&lt;p>For tech professionals actively looking to leave their current roles for better opportunities, the wave of redundancies presents a frustrating Catch-22. While there might be new roles opening due to the layoffs, the dynamics of the job market have shifted:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Extended Notice Periods:&lt;/strong> Individuals in stable employment are often bound by notice periods, which can range from weeks to months. Meanwhile, those made redundant are immediately available, giving them a significant advantage in a competitive job market.&lt;/li>
&lt;li>&lt;strong>Missed Opportunities:&lt;/strong> By the time someone with a notice period is free to start, the roles they were interested in might already be filled by candidates who were made redundant and could start immediately.&lt;/li>
&lt;li>&lt;strong>Feeling Trapped:&lt;/strong> This situation can leave those wanting to move feeling stuck and resentful, further contributing to dissatisfaction and potential burnout in their current roles.&lt;/li>
&lt;/ul>
&lt;p>The irony is palpable: individuals seeking growth and new challenges are penalised for their current employment stability in a market flooded with immediately available talent.&lt;/p>
&lt;h3 id="the-downward-pressure-on-salaries-a-threat-to-earning-potential">The Downward Pressure on Salaries: A Threat to Earning Potential&lt;/h3>
&lt;p>Perhaps one of the most concerning long-term effects of widespread redundancies is the potential downward pressure on salaries across the tech industry. This happens because:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Increased Supply of Talent:&lt;/strong> A large pool of skilled tech professionals suddenly enters the job market.&lt;/li>
&lt;li>&lt;strong>Willingness to Accept Lower Pay:&lt;/strong> Some of those made redundant may be in a position where they &lt;em>need&lt;/em> to secure employment quickly, making them more willing to accept salaries lower than their previous earnings or the prevailing market rate.&lt;/li>
&lt;li>&lt;strong>Employer Leverage:&lt;/strong> Companies looking to hire can leverage this increased supply and the willingness of some candidates to accept less, potentially driving down salary offers for everyone.&lt;/li>
&lt;/ul>
&lt;p>This erosion of salary expectations can have a significant impact on the financial wellbeing of all tech workers, not just those who were laid off. It can lead to:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Reduced earning potential:&lt;/strong> Even those who remain employed or find new roles may find it harder to negotiate competitive salaries.&lt;/li>
&lt;li>&lt;strong>Decreased morale and motivation:&lt;/strong> Feeling undervalued due to stagnant or declining salary prospects can further contribute to burnout and dissatisfaction.&lt;/li>
&lt;li>&lt;strong>A less attractive industry:&lt;/strong> In the long run, lower salaries could make the tech industry less attractive to new talent, potentially hindering innovation and growth.&lt;/li>
&lt;/ul>
&lt;h3 id="the-vicious-cycle-redundancies-fuelling-more-burnout">The Vicious Cycle: Redundancies Fuelling More Burnout&lt;/h3>
&lt;p>The interconnectedness of these factors creates a dangerous cycle that can significantly worsen the existing burnout crisis in the tech industry. Increased workloads, stifled career movement, and the fear of salary stagnation or decline all contribute to a more stressful and less rewarding work environment.&lt;/p>
&lt;p>When people feel overworked, undervalued, and trapped, their motivation plummets, and their risk of burnout skyrockets. This can lead to decreased productivity, higher turnover in the long run (even among those who weren&amp;rsquo;t initially laid off), and a less vibrant and innovative tech ecosystem.&lt;/p>
&lt;h3 id="looking-ahead-a-call-for-empathy-and-sustainable-practices">Looking Ahead: A Call for Empathy and Sustainable Practices&lt;/h3>
&lt;p>While companies may see redundancies as a necessary measure in challenging economic times, it&amp;rsquo;s crucial to recognise the far-reaching consequences. Ignoring the impact on remaining employees and the broader talent pool is a short-sighted approach that can have detrimental long-term effects.&lt;/p>
&lt;p>Moving forward, a greater emphasis on sustainable growth, fair treatment of all employees (both departing and remaining), and a recognition of the human cost of these decisions is essential. Only then can the tech industry hope to mitigate the negative ripple effects of redundancies and foster a healthier, more resilient, and ultimately more productive environment for everyone.&lt;/p></description></item><item><title>AI is Making Developers Lazy: RIP Core Coding Skills</title><link>https://www.darrenhorrocks.co.uk/ai-making-developers-lazy-rip-core-coding-skills/</link><pubDate>Wed, 07 May 2025 12:53:44 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/ai-making-developers-lazy-rip-core-coding-skills/</guid><description>&lt;p>The hum of the AI co-pilot has become a familiar soundtrack in the world of software development. These intelligent tools, promising increased efficiency and code generation prowess, have been embraced with open arms by many. But what happens when this reliance morphs into over-dependence? What are the potential pitfalls of blindly trusting algorithms we don&amp;rsquo;t fully comprehend, especially when they occasionally – or even frequently – get it wrong? And perhaps most worryingly, what becomes of the core skills that define a truly capable software developer?&lt;/p>
&lt;p>The allure is undeniable. Imagine effortlessly generating boilerplate code, instantly debugging complex errors, or even having entire modules sketched out for you. AI tools offer the promise of freeing up developers from repetitive tasks, allowing them to focus on higher-level problem-solving and innovation. However, this convenience comes with a potential Faustian bargain.&lt;/p>
&lt;h3 id="the-black-box-problem-trusting-what-we-dont-understand">The Black Box Problem: Trusting What We Don&amp;rsquo;t Understand&lt;/h3>
&lt;p>Many of the sophisticated AI models powering these tools operate as &amp;ldquo;black boxes.&amp;rdquo; We feed them data and receive output, but the intricate web of algorithms and decision-making processes within remains opaque. This lack of transparency can be problematic, especially when the AI generates incorrect or suboptimal code.&lt;/p>
&lt;p>If developers don&amp;rsquo;t possess a strong foundational understanding of the underlying principles, how can they effectively evaluate the AI&amp;rsquo;s suggestions? How can they discern a clever solution from a flawed one? The danger lies in blindly accepting AI-generated code without critical review, potentially introducing bugs, security vulnerabilities, and inefficient designs into the codebase.&lt;/p>
&lt;h3 id="the-erosion-of-core-skills-a-muscle-unused-withers">The Erosion of Core Skills: A Muscle Unused Withers&lt;/h3>
&lt;p>Software development is a craft built upon a foundation of problem-solving, logical reasoning, algorithmic thinking, and a deep understanding of programming paradigms. These core skills are honed through years of practice, experimentation, and the occasional struggle.&lt;/p>
&lt;p>When developers become overly reliant on AI to generate code and solve problems, these fundamental skills can atrophy. Imagine a musician who always relies on auto-tune; their natural pitch and vocal control will likely diminish over time. Similarly, a developer who constantly leans on AI might lose the ability to:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Think algorithmically:&lt;/strong> Understanding how to break down complex problems into logical steps becomes less crucial if an AI can seemingly generate the solution.&lt;/li>
&lt;li>&lt;strong>Debug effectively:&lt;/strong> If developers haven&amp;rsquo;t developed the intuition for identifying and tracing errors themselves, they may struggle when the AI&amp;rsquo;s suggestions are unhelpful or misleading.&lt;/li>
&lt;li>&lt;strong>Write clean and efficient code:&lt;/strong> Over-reliance on AI might lead to a neglect of coding best practices, resulting in bloated, difficult-to-maintain codebases.&lt;/li>
&lt;li>&lt;strong>Understand system architecture:&lt;/strong> A superficial understanding of how different components interact might suffice if AI is handling the intricate details.&lt;/li>
&lt;li>&lt;strong>Adapt to new technologies:&lt;/strong> The rapid evolution of the tech landscape demands continuous learning and adaptation. Developers who haven&amp;rsquo;t cultivated strong learning skills might find themselves struggling when AI tools don&amp;rsquo;t yet support the latest advancements.&lt;/li>
&lt;/ul>
&lt;h3 id="the-wrong-factor-ais-inherent-imperfection">The &amp;ldquo;Wrong&amp;rdquo; Factor: AI&amp;rsquo;s Inherent Imperfection&lt;/h3>
&lt;p>It&amp;rsquo;s crucial to remember that current AI tools are not infallible. They learn from data, and if that data contains biases or errors, the AI will likely perpetuate them. Furthermore, AI models can sometimes generate syntactically correct but semantically flawed code, or suggest solutions that are technically feasible but architecturally unsound.&lt;/p>
&lt;p>If developers lack the critical thinking skills to identify these inaccuracies, they risk building flawed systems based on faulty AI outputs. This can lead to costly rework, security breaches, and ultimately, a decrease in the quality and reliability of software.&lt;/p>
&lt;h3 id="finding-the-balance-augmentation-not-replacement">Finding the Balance: Augmentation, Not Replacement&lt;/h3>
&lt;p>The future of software development likely involves a symbiotic relationship between humans and AI. These tools have the potential to be powerful &lt;em>augmentations&lt;/em> to our abilities, freeing us from tedious tasks and offering new perspectives. However, the key lies in maintaining a strong foundation of core development skills.&lt;/p>
&lt;p>Developers should approach AI tools with a healthy dose of skepticism and a commitment to understanding the &amp;ldquo;why&amp;rdquo; behind the &amp;ldquo;what.&amp;rdquo; We must remain active participants in the development process, using AI as a powerful assistant rather than a complete replacement for our own expertise.&lt;/p>
&lt;h3 id="moving-forward-cultivating-critical-thinking-in-the-age-of-ai">Moving Forward: Cultivating Critical Thinking in the Age of AI&lt;/h3>
&lt;p>To avoid the pitfalls of over-reliance, the software development community needs to prioritize:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Emphasis on fundamental skills:&lt;/strong> Educational institutions and training programs should continue to stress the importance of core programming principles and problem-solving techniques.&lt;/li>
&lt;li>&lt;strong>Promoting critical evaluation:&lt;/strong> Developers should be encouraged to critically analyze AI-generated code and understand its implications.&lt;/li>
&lt;li>&lt;strong>Continuous learning:&lt;/strong> Staying abreast of both AI advancements and fundamental development practices is crucial.&lt;/li>
&lt;li>&lt;strong>Transparency and explainability:&lt;/strong> The development of more transparent and explainable AI models will be vital in fostering trust and understanding.&lt;/li>
&lt;/ul>
&lt;p>The rise of AI in software development presents both exciting opportunities and potential dangers. By embracing these tools responsibly and ensuring that our core skills remain sharp, we can harness the power of AI to build better software without sacrificing the expertise and ingenuity that defines us as developers. The algorithmic crutch might offer temporary relief, but true mastery lies in our own understanding and ability to craft elegant and robust solutions.&lt;/p></description></item><item><title>The Optimisation Lie: Why Your 'Optimised' Code Might Still Be Slow</title><link>https://www.darrenhorrocks.co.uk/optimisation-lie-why-your-optimised-code-might-still-be-slow/</link><pubDate>Thu, 01 May 2025 09:57:02 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/optimisation-lie-why-your-optimised-code-might-still-be-slow/</guid><description>&lt;p>We&amp;rsquo;ve all been there. You&amp;rsquo;ve tweaked a loop, maybe used a more efficient LINQ method, and patted yourself on the back for &amp;ldquo;optimising&amp;rdquo; your C# code. The profiler might even show a slight improvement. But then, the application still feels… sluggish. That&amp;rsquo;s because the initial steps in optimisation can be deceptively easy, leading to what we&amp;rsquo;ll call &lt;strong>The Optimisation Lie&lt;/strong>, the belief that a few superficial changes equate to truly well-optimised software.&lt;/p>
&lt;p>The truth is, while applying basic optimisations in C# is often straightforward, achieving significant and sustainable performance gains requires a much deeper understanding and a more strategic approach.&lt;/p>
&lt;h3 id="the-siren-song-of-simple-c-optimisations">The Siren Song of Simple C# Optimisations&lt;/h3>
&lt;p>C# offers many seemingly easy ways to improve performance at a glance:&lt;/p>
&lt;h4 id="string-concatenation">String Concatenation&lt;/h4>
&lt;p>Switching from repeated &lt;code>+&lt;/code> operations to &lt;code>StringBuilder&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// The &amp;#34;easy&amp;#34; fix
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">string&lt;/span> message = &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; &lt;span style="color:#ae81ff">1000&lt;/span>; i++)
{
message += i.ToString(); &lt;span style="color:#75715e">// Inefficient
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#75715e">// The slightly better approach
&lt;/span>&lt;span style="color:#75715e">&lt;/span>StringBuilder sb = &lt;span style="color:#66d9ef">new&lt;/span> StringBuilder();
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; &lt;span style="color:#ae81ff">1000&lt;/span>; i++)
{
sb.Append(i.ToString());
}
message = sb.ToString();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is a common and valid initial optimisation, but it might not be the biggest bottleneck in your application.&lt;/p>
&lt;h4 id="linq-efficiency">LINQ Efficiency&lt;/h4>
&lt;p>Choosing the &amp;ldquo;right&amp;rdquo; LINQ method.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Potentially less efficient
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> expensiveResult = myList.Where(x =&amp;gt; x &amp;gt; &lt;span style="color:#ae81ff">10&lt;/span>).Select(y =&amp;gt; y * &lt;span style="color:#ae81ff">2&lt;/span>).ToList();
&lt;span style="color:#75715e">// Potentially more efficient (depending on the scenario)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> betterResult = (&lt;span style="color:#66d9ef">from&lt;/span> x &lt;span style="color:#66d9ef">in&lt;/span> myList &lt;span style="color:#66d9ef">where&lt;/span> x &amp;gt; &lt;span style="color:#ae81ff">10&lt;/span> &lt;span style="color:#66d9ef">select&lt;/span> x * &lt;span style="color:#ae81ff">2&lt;/span>).ToList();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>While LINQ can be powerful, understanding its underlying execution and potential overhead is crucial. Simply switching methods without profiling might not yield significant gains.&lt;/p>
&lt;h4 id="basic-caching">Basic Caching&lt;/h4>
&lt;p>Implementing a simple &lt;code>Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code> for frequently accessed data.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>, User&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>userCache = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>, User&amp;gt;();
&lt;span style="color:#66d9ef">public&lt;/span> User GetUser(&lt;span style="color:#66d9ef">int&lt;/span> id)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#ae81ff">_&lt;/span>userCache.ContainsKey(id))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">_&lt;/span>userCache[id]; &lt;span style="color:#75715e">// Easy cache hit
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
User user = FetchUserFromDatabase(id);
&lt;span style="color:#ae81ff">_&lt;/span>userCache[id] = user;
&lt;span style="color:#66d9ef">return&lt;/span> user;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Basic caching is easy to implement, but managing cache invalidation, concurrency, and memory usage effectively is far more complex.&lt;/p>
&lt;p>These &amp;ldquo;easy wins&amp;rdquo; can provide a sense of accomplishment, but they often scratch the surface of underlying performance issues.&lt;/p>
&lt;h3 id="the-treacherous-path-to-genuine-c-optimisation">The Treacherous Path to Genuine C# Optimisation&lt;/h3>
&lt;p>Optimising C# code &lt;em>well&lt;/em> requires a more holistic and data-driven approach:&lt;/p>
&lt;h4 id="identifying-the-real-bottleneck-with-profiling">Identifying the Real Bottleneck with Profiling&lt;/h4>
&lt;p>Guesswork is the enemy of good optimisation. Tools like the Visual Studio Profiler, PerfView, or JetBrains dotTrace are essential to pinpoint the &lt;em>actual&lt;/em> performance bottlenecks in your C# application. Is it database access, network I/O, garbage collection, or a specific algorithm?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Example: Using Stopwatch for basic timing
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> stopwatch = Stopwatch.StartNew();
&lt;span style="color:#75715e">// Code block to profile
&lt;/span>&lt;span style="color:#75715e">&lt;/span>stopwatch.Stop();
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Elapsed Time: {stopwatch.ElapsedMilliseconds} ms&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Real profiling tools provide much more granular insights.&lt;/p>
&lt;h4 id="understanding-asynchronous-operations-asyncawait">Understanding Asynchronous Operations (async/await)&lt;/h4>
&lt;p>In I/O-bound operations (like network requests or file access), blocking the main thread can lead to significant performance issues and UI freezes. Mastering &lt;code>async&lt;/code> and &lt;code>await&lt;/code> in C# is crucial for responsiveness.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>&amp;gt; DownloadDataAsync(&lt;span style="color:#66d9ef">string&lt;/span> url)
{
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> httpClient = &lt;span style="color:#66d9ef">new&lt;/span> HttpClient())
{
&lt;span style="color:#66d9ef">var&lt;/span> response = &lt;span style="color:#66d9ef">await&lt;/span> httpClient.GetAsync(url); &lt;span style="color:#75715e">// Non-blocking
&lt;/span>&lt;span style="color:#75715e">&lt;/span> response.EnsureSuccessStatusCode();
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> response.Content.ReadAsStringAsync(); &lt;span style="color:#75715e">// Non-blocking
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Simply using &lt;code>async&lt;/code> without understanding its implications (e.g., thread context switching) might not yield optimal results.&lt;/p>
&lt;h4 id="efficient-data-structures-and-algorithms">Efficient Data Structures and Algorithms&lt;/h4>
&lt;p>Choosing the right data structure (e.g., &lt;code>HashSet&lt;/code> for fast lookups, &lt;code>List&lt;/code> for ordered collections) and algorithms (e.g., avoiding nested loops where a more efficient approach exists) can have a massive impact on performance, especially with large datasets.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Inefficient search in a List
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">bool&lt;/span> containsItemSlow = myList.Contains(someItem); &lt;span style="color:#75715e">// O(n)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">// Efficient search in a HashSet
&lt;/span>&lt;span style="color:#75715e">&lt;/span>HashSet&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>&amp;gt; mySet = &lt;span style="color:#66d9ef">new&lt;/span> HashSet&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(myList);
&lt;span style="color:#66d9ef">bool&lt;/span> containsItemFast = mySet.Contains(someItem); &lt;span style="color:#75715e">// O(1) on average
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="memory-management-and-garbage-collection">Memory Management and Garbage Collection&lt;/h4>
&lt;p>Understanding how the .NET garbage collector works and minimising unnecessary object allocations can significantly improve performance and reduce pauses. Techniques like object pooling or using &lt;code>struct&lt;/code> for small, value-type data can be beneficial in specific scenarios.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Example of potential excessive object creation
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; &lt;span style="color:#ae81ff">1000&lt;/span>; i++)
{
&lt;span style="color:#66d9ef">string&lt;/span> temp = &lt;span style="color:#e6db74">&amp;#34;prefix-&amp;#34;&lt;/span> + i.ToString(); &lt;span style="color:#75715e">// Creates a new string in each iteration
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// ... use temp ...
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="database-optimisation">Database Optimisation&lt;/h4>
&lt;p>Often, the biggest performance bottlenecks lie in database interactions. This involves writing efficient SQL queries, using appropriate indexing, and understanding your database&amp;rsquo;s execution plan. While not strictly C# code, it&amp;rsquo;s a critical part of many C# applications.&lt;/p>
&lt;h4 id="concurrency-and-parallelism-task-parallel-library---tpl">Concurrency and Parallelism (Task Parallel Library - TPL)&lt;/h4>
&lt;p>Leveraging multi-core processors with the TPL (&lt;code>Task.Run&lt;/code>, &lt;code>Parallel.For/ForEach&lt;/code>) can dramatically improve performance for CPU-bound tasks. However, managing shared state, avoiding race conditions, and understanding the overhead of task creation are essential for effective parallelism.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Parallel processing using Parallel.ForEach
&lt;/span>&lt;span style="color:#75715e">&lt;/span>Parallel.ForEach(dataList, item =&amp;gt;
{
&lt;span style="color:#75715e">// Perform CPU-bound operation on each item
&lt;/span>&lt;span style="color:#75715e">&lt;/span> ProcessItem(item);
});
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Poorly implemented parallelism can lead to performance degradation and hard-to-debug errors.&lt;/p>
&lt;h4 id="context-and-measurement-are-king">Context and Measurement are King&lt;/h4>
&lt;p>Optimisation efforts must be driven by clear performance goals and rigorous measurement. What are the acceptable response times? What are the resource constraints? Without benchmarking and profiling &lt;em>before&lt;/em> and &lt;em>after&lt;/em> optimisations, you&amp;rsquo;re flying blind. Tools like BenchmarkDotNet are invaluable for precise performance measurements in C#.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#a6e22e">[MemoryDiagnoser]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">StringVsStringBuilder&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> N = &lt;span style="color:#ae81ff">1000&lt;/span>;
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [Benchmark]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> ConcatStrings()
{
&lt;span style="color:#66d9ef">string&lt;/span> message = &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; N; i++)
{
message += i.ToString();
}
&lt;span style="color:#66d9ef">return&lt;/span> message;
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [Benchmark]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> StringBuilderAppend()
{
StringBuilder sb = &lt;span style="color:#66d9ef">new&lt;/span> StringBuilder();
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; N; i++)
{
sb.Append(i.ToString());
}
&lt;span style="color:#66d9ef">return&lt;/span> sb.ToString();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>The Art of True C# Optimisation:&lt;/strong>&lt;/p>
&lt;p>Effective C# optimisation is a continuous process of analysis, strategic implementation, and meticulous measurement. It&amp;rsquo;s about understanding the underlying mechanisms of the .NET runtime, choosing the right tools and techniques for the specific problem, and always validating your assumptions with data.&lt;/p>
&lt;p>Don&amp;rsquo;t fall for the optimisation lie. While those initial quick fixes might offer a small boost, achieving truly performant and scalable C# applications requires a deeper dive and a commitment to data-driven decision-making. So, equip yourself with profiling tools, understand the intricacies of asynchronous programming and memory management, and always measure the impact of your changes. That&amp;rsquo;s the path to unlocking the true potential of your C# code.&lt;/p></description></item><item><title>The Dirty Secret of Clean Code: When Breaking Programming Principles Makes You a Better Developer</title><link>https://www.darrenhorrocks.co.uk/dirty-secret-clean-code-when-breaking-programming-principles-makes-you-better-developer/</link><pubDate>Tue, 08 Apr 2025 13:02:53 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/dirty-secret-clean-code-when-breaking-programming-principles-makes-you-better-developer/</guid><description>&lt;p>If you&amp;rsquo;ve been in software development for more than five minutes, you&amp;rsquo;ve probably had these acronyms beaten into your head:&lt;/p>
&lt;p>&lt;strong>SOLID:&lt;/strong> that five-headed monster of principles that&amp;rsquo;s supposed to make your code amazing.&lt;/p>
&lt;p>&lt;strong>DRY:&lt;/strong> because apparently typing the same logic twice will summon demons or something.&lt;/p>
&lt;p>Don&amp;rsquo;t get me wrong, these principles exist for good reasons! They&amp;rsquo;ve saved countless developers from nightmare codebases. But here&amp;rsquo;s the kicker: &lt;strong>they&amp;rsquo;re guidelines, not commandments handed down from the mountain&lt;/strong>.&lt;/p>
&lt;h2 id="when-its-totally-fine-to-break-the-rules">When It&amp;rsquo;s Totally Fine to &amp;ldquo;Break the Rules&amp;rdquo;&lt;/h2>
&lt;p>Look, we&amp;rsquo;ve all been there. You&amp;rsquo;re coding away, trying to follow all those fancy principles they taught you, and suddenly you find yourself twisting your code into pretzels just to satisfy some abstract concept like &amp;ldquo;Single Responsibility&amp;rdquo; or &amp;ldquo;Don&amp;rsquo;t Repeat Yourself.&amp;rdquo;&lt;/p>
&lt;p>And you know what? Sometimes that&amp;rsquo;s completely unnecessary.&lt;/p>
&lt;h3 id="when-you-need-to-ship-yesterday">When You Need to Ship Yesterday&lt;/h3>
&lt;p>Let&amp;rsquo;s be real: sometimes you just need to get that feature out the door. If you&amp;rsquo;re staring at a tight deadline and your code works but isn&amp;rsquo;t perfectly SOLID, guess what? That&amp;rsquo;s life. Ship it. Fix it later if it causes problems.&lt;/p>
&lt;h3 id="when-youre-just-figuring-stuff-out">When You&amp;rsquo;re Just Figuring Stuff Out&lt;/h3>
&lt;p>Early in a project, you barely know what the code needs to do, let alone how to structure it perfectly. Trying to make everything SOLID before you even understand the problem is like buying wedding rings on a first date.&lt;/p>
&lt;h3 id="when-perfect-code-means-perfectly-unreadable">When Perfect Code Means Perfectly Unreadable&lt;/h3>
&lt;p>I&amp;rsquo;ve seen codebases where developers followed DRY so religiously that understanding what any function actually did required tracing through fifteen layers of abstraction. Sometimes a little repetition makes code way easier to understand.&lt;/p>
&lt;h3 id="when-performance-actually-matters">When Performance Actually Matters&lt;/h3>
&lt;p>&amp;ldquo;But what about the performance hit from all those tiny functions?&amp;rdquo; If you&amp;rsquo;re writing code for a toaster with 2KB of RAM, or handling millions of operations per second, sometimes breaking SRP is the right call.&lt;/p>
&lt;h2 id="the-real-world-example">The Real-World Example&lt;/h2>
&lt;p>Last month, I was working on this dashboard feature. According to SOLID, I should&amp;rsquo;ve split this one class into like, five different classes with their own interfaces. But you know what? It was one specific feature that wasn&amp;rsquo;t going to be reused elsewhere. Breaking it apart would&amp;rsquo;ve just scattered related code for no real benefit.&lt;/p>
&lt;p>So I kept it as one chunky class that did several things. Scandalous, I know! But guess what? It works great, everyone understands it, and the world didn&amp;rsquo;t end.&lt;/p>
&lt;h2 id="finding-your-balance">Finding Your Balance&lt;/h2>
&lt;p>The secret sauce of being a good developer isn&amp;rsquo;t memorising rules – it&amp;rsquo;s knowing when to apply them and when to say &amp;ldquo;nah, not this time.&amp;rdquo;&lt;/p>
&lt;p>Ask yourself:&lt;/p>
&lt;ul>
&lt;li>Will this abstraction actually help anyone, or am I just doing it to feel smart?&lt;/li>
&lt;li>Is this code going to change often, or is it pretty stable?&lt;/li>
&lt;li>Will splitting this make it easier or harder for the next person to understand?&lt;/li>
&lt;li>Am I solving real problems or imaginary future ones?&lt;/li>
&lt;/ul>
&lt;h2 id="the-bottom-line">The Bottom Line&lt;/h2>
&lt;p>Programming principles are like those pirate code guidelines from Pirates of the Caribbean – they&amp;rsquo;re more like suggestions than actual rules. The end goal is working, maintainable software that solves real problems – not code that wins theoretical purity contests.&lt;/p>
&lt;p>Next time you find yourself going through backflips to satisfy some principle, ask yourself if it&amp;rsquo;s actually making your code better or just making you feel better.&lt;/p>
&lt;p>What coding &amp;ldquo;rules&amp;rdquo; have you broken lately? Drop a comment – I promise not to report you to the code police!&lt;/p></description></item><item><title>AI Is Smart Because You Are Stupid</title><link>https://www.darrenhorrocks.co.uk/ai-is-smart-because-you-are-stupid/</link><pubDate>Sun, 30 Mar 2025 21:55:08 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/ai-is-smart-because-you-are-stupid/</guid><description>&lt;p>Artificial Intelligence is often perceived as a technological marvel, a near-mystical force that can predict, automate, and generate at an astonishing rate. However, this perception isn&amp;rsquo;t necessarily because AI is truly “intelligent” in the way humans are. Rather, AI appears good because humans, by comparison, are inconsistent, distracted, and prone to errors. The brilliance of AI is not in its innate intelligence, but in how it exploits human shortcomings.&lt;/p>
&lt;h2 id="the-illusion-of-intelligence">The Illusion of Intelligence&lt;/h2>
&lt;p>AI, at its core, is a set of algorithms trained on vast datasets to recognise patterns, generate responses, and optimise tasks based on statistical probabilities. It does not “think,” nor does it possess creativity or consciousness. Yet, when AI completes tasks efficiently - be it answering questions, summarising documents, or even generating art - it seems astonishingly capable. This is because it operates without fatigue, bias (at least in theory), or hesitation, whereas humans are plagued by these very limitations.&lt;/p>
&lt;p>Think about it: an AI can generate a well-structured essay in seconds, but how often do humans struggle with writer&amp;rsquo;s block? AI can recognise patterns in financial markets faster than any expert, but how often do humans make emotional investment decisions? The disparity isn&amp;rsquo;t necessarily because AI is “smart”; it&amp;rsquo;s because humans, in comparison, are imprecise and inefficient.&lt;/p>
&lt;h2 id="the-dependence-problem">The Dependence Problem&lt;/h2>
&lt;p>Because AI highlights human shortcomings, many people have come to rely on it for tasks that they could otherwise accomplish themselves. Spellcheck and grammar tools improve writing that should be polished by the writer. AI-assisted coding corrects errors that developers might have caught with proper debugging. Recommendation algorithms dictate consumer choices, reducing the need for personal decision-making. The issue isn&amp;rsquo;t that AI is better - it&amp;rsquo;s that people have lowered their standards and delegated tasks they no longer want to engage with fully.&lt;/p>
&lt;h2 id="a-reflection-of-human-limitations">A Reflection of Human Limitations&lt;/h2>
&lt;p>AI&amp;rsquo;s seeming intelligence is, in many ways, a mirror that reflects our own intellectual laziness. It is easy to be impressed by a chatbot&amp;rsquo;s ability to hold a coherent conversation - until you realise that much of human conversation is just as formulaic and predictable. We praise AI for generating stunning images, but we fail to acknowledge that creativity itself is often just an assembly of existing concepts. AI highlights the shortcuts that humans wish they could take but lack the processing speed to execute.&lt;/p>
&lt;h2 id="the-challenge-for-the-future">The Challenge for the Future&lt;/h2>
&lt;p>Instead of being in awe of AI&amp;rsquo;s capabilities, we should ask ourselves what this technology reveals about us. Are we allowing AI to replace critical thinking? Are we using it as a tool for enhancement or as a crutch for laziness? The danger isn&amp;rsquo;t that AI is becoming too intelligent - it&amp;rsquo;s that humans are becoming too complacent.&lt;/p>
&lt;p>AI seems good because it compensates for our inefficiencies. The real question is: will we use it to elevate ourselves, or will we allow it to replace what makes us uniquely human?&lt;/p></description></item><item><title>.NET Aspire - Microsoft's Attempt at Infrastructure as Code</title><link>https://www.darrenhorrocks.co.uk/net-aspire-microsofts-attempt-at-infrastructure-as-code/</link><pubDate>Fri, 28 Mar 2025 10:21:43 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/net-aspire-microsofts-attempt-at-infrastructure-as-code/</guid><description>&lt;p>.NET Aspire is a new set of tools, templates, and packages from Microsoft designed to make building cloud-native applications with .NET easier and more efficient. It helps developers create applications that are resilient, observable, and ready for production.&lt;/p>
&lt;h2 id="what-is-net-aspire">What is .NET Aspire?&lt;/h2>
&lt;p>Traditionally, developers wrote code while operations teams managed the infrastructure it ran on. This separation could lead to challenges in ensuring that applications and their environments worked well together. .NET Aspire aims to bridge this gap by allowing developers to define both their applications and the necessary infrastructure in a unified way.&lt;/p>
&lt;h2 id="how-does-net-aspire-work">How Does .NET Aspire Work?&lt;/h2>
&lt;p>In a .NET Aspire project, you can define various components like databases, caches, and APIs, and specify how they connect. This is done in a central place called the &amp;ldquo;app host,&amp;rdquo; which acts as an orchestrator for your application. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> builder = DistributedApplication.CreateBuilder(args);
&lt;span style="color:#66d9ef">var&lt;/span> postgres = builder.AddPostgres(&lt;span style="color:#e6db74">&amp;#34;postgres&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> catalogDb = postgres.AddDatabase(&lt;span style="color:#e6db74">&amp;#34;catalogdb&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> basketCache = builder.AddRedis(&lt;span style="color:#e6db74">&amp;#34;basketcache&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> productsApi = builder.AddProject&amp;lt;Projects.Aspire_Products_Api&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;productsapi&amp;#34;&lt;/span>)
.WithReference(catalogDb);
&lt;span style="color:#66d9ef">var&lt;/span> basketApi = builder.AddProject&amp;lt;Projects.Aspire_Basket_Api&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;basketapi&amp;#34;&lt;/span>)
.WithReference(basketCache);
builder.AddProject&amp;lt;Projects.Aspire_Frontend&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;frontend&amp;#34;&lt;/span>)
.WithReference(basketApi)
.WithReference(productsApi);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this code, a PostgreSQL database and a Redis cache are set up. These are then linked to the appropriate backend APIs for products and baskets. Finally, the frontend application is connected to these backend services. This setup ensures that each part of the application knows how to communicate with the others without needing to know specific URLs or connection details.&lt;/p>
&lt;h2 id="what-does-this-mean-for-infrastructure-as-code-iac">What Does This Mean for Infrastructure as Code (IaC)?&lt;/h2>
&lt;p>Infrastructure as Code (IaC) tools like ARM templates, Terraform, or Pulumi have traditionally been used to define and manage infrastructure separately from application code. With .NET Aspire, some of this infrastructure definition moves into the application code itself. For example, you can define role-based access controls (RBAC) directly within your application:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> storage = builder.AddAzureStorage(&lt;span style="color:#e6db74">&amp;#34;mystorage&amp;#34;&lt;/span>)
.WithRoleAssignment(&lt;span style="color:#e6db74">&amp;#34;StorageBlobDataOwner&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;myapp&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This approach allows developers to manage integrations, roles, and boundaries directly within their applications. However, it&amp;rsquo;s important to carefully manage security and coordinate between application-level and platform-level IaC to ensure a secure and efficient system.&lt;/p>
&lt;h2 id="where-does-iac-fit-in">Where Does IaC Fit In?&lt;/h2>
&lt;p>While .NET Aspire enables developers to define much of their application&amp;rsquo;s infrastructure, traditional IaC tools still play a crucial role in managing broader infrastructure aspects like networking, firewalls, and overall system isolation. IaC ensures that the system is securely set up and provides the necessary access points for applications to function correctly.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>.NET Aspire represents a shift towards integrating application development and infrastructure management. By allowing developers to define infrastructure needs within their application code, it provides greater control and streamlines the development process. However, traditional IaC tools remain essential for managing the broader infrastructure environment, ensuring security, and maintaining system integrity.&lt;/p></description></item><item><title>Understanding Garbage Collection - How the dotnet Garbage Collector Works</title><link>https://www.darrenhorrocks.co.uk/understanding-garbage-collection-how-dotnet-garbage-collector-works/</link><pubDate>Wed, 05 Feb 2025 13:27:20 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/understanding-garbage-collection-how-dotnet-garbage-collector-works/</guid><description>&lt;p>Garbage collection (GC) is a fundamental component of the .NET runtime, responsible for managing memory automatically and ensuring efficient use of resources. With .NET 8, Microsoft has continued refining the garbage collector, improving performance, reducing latency, and enhancing overall efficiency. This article dives into how the .NET 8 GC works, its key improvements, and how developers can optimise memory usage.&lt;/p>
&lt;h2 id="how-the-net-8-garbage-collector-works">How the .NET 8 Garbage Collector Works&lt;/h2>
&lt;p>The .NET 8 garbage collector operates as a generational, concurrent, and compacting GC. This means it divides objects into different generations based on their lifespan, collects garbage in a way that minimises application pauses, and compacts memory to reduce fragmentation. Here’s a breakdown of its key components:&lt;/p>
&lt;h3 id="generational-garbage-collection">&lt;strong>Generational Garbage Collection&lt;/strong>&lt;/h3>
&lt;p>.NET’s GC follows a generational approach, classifying objects into three generations:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Generation 0 (Gen 0):&lt;/strong> Short-lived objects such as temporary variables and method-scoped objects.&lt;/li>
&lt;li>&lt;strong>Generation 1 (Gen 1):&lt;/strong> Objects that survived at least one GC cycle, acting as a buffer between Gen 0 and Gen 2.&lt;/li>
&lt;li>&lt;strong>Generation 2 (Gen 2):&lt;/strong> Long-lived objects such as static data, caches, and application-wide objects.&lt;/li>
&lt;/ul>
&lt;p>When the GC runs, it primarily collects Gen 0 objects, since they are short-lived. If memory pressure persists, it progresses to Gen 1 and eventually to a full GC (Gen 2), which is more expensive.&lt;/p>
&lt;h3 id="concurrent-and-background-collection">&lt;strong>Concurrent and Background Collection&lt;/strong>&lt;/h3>
&lt;p>To improve performance, .NET 8’s GC includes background collection:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Concurrent GC:&lt;/strong> Allows the application to continue running while the GC reclaims memory.&lt;/li>
&lt;li>&lt;strong>Background GC:&lt;/strong> Used for Gen 2 collections to minimise blocking, keeping latency low for large applications.&lt;/li>
&lt;/ul>
&lt;p>These optimisations are particularly beneficial for real-time applications like web servers and gaming engines, where low-latency memory management is critical.&lt;/p>
&lt;h3 id="compacting-and-memory-fragmentation-reduction">&lt;strong>Compacting and Memory Fragmentation Reduction&lt;/strong>&lt;/h3>
&lt;p>As objects are allocated and freed, memory fragmentation can occur. The .NET GC mitigates this by compacting memory, moving surviving objects closer together to create larger free memory blocks. This improves cache locality and reduces memory pressure.&lt;/p>
&lt;h3 id="pinned-objects-and-loh-large-object-heap-handling">&lt;strong>Pinned Objects and LOH (Large Object Heap) Handling&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Pinned Objects:&lt;/strong> Some objects (e.g., those used in interop with native code) cannot be moved, which can lead to fragmentation. The GC optimises handling of pinned objects to reduce their impact on performance.&lt;/li>
&lt;li>&lt;strong>Large Object Heap (LOH):&lt;/strong> Objects larger than 85 KB are allocated in the LOH. .NET 8 includes improved LOH compaction to prevent excessive fragmentation.&lt;/li>
&lt;/ul>
&lt;h2 id="whats-new-in-net-8-garbage-collection">What’s New in .NET 8 Garbage Collection?&lt;/h2>
&lt;p>.NET 8 introduces several key improvements to the GC:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Better Workload-Based Optimisations:&lt;/strong> The GC now adapts more effectively to different workloads, making it more efficient in cloud and high-performance applications.&lt;/li>
&lt;li>&lt;strong>Improved Memory Compaction Heuristics:&lt;/strong> Smarter algorithms decide when to compact memory, reducing unnecessary performance overhead.&lt;/li>
&lt;li>&lt;strong>Reduced Lock Contention in Multi-Threaded Environments:&lt;/strong> Enhancements in the GC’s locking mechanisms help prevent contention, particularly in multi-core environments.&lt;/li>
&lt;li>&lt;strong>Lower Latency for Interactive Applications:&lt;/strong> Optimisations ensure that UI and real-time applications experience fewer interruptions due to garbage collection.&lt;/li>
&lt;/ul>
&lt;h2 id="how-to-optimise-for-net-8s-gc">How to Optimise for .NET 8’s GC&lt;/h2>
&lt;p>While the .NET GC is highly optimised, developers can still take steps to improve performance:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Reduce Unnecessary Object Allocations&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Use &lt;strong>structs&lt;/strong> instead of classes for small, short-lived objects.&lt;/li>
&lt;li>Avoid excessive boxing/unboxing of value types.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Use Object Pooling&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Consider using &lt;code>ObjectPool&amp;lt;T&amp;gt;&lt;/code> from &lt;code>Microsoft.Extensions.ObjectPool&lt;/code> to reuse objects instead of frequently allocating and deallocating them.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Minimise Large Object Heap Usage&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Keep objects under 85kb when possible to avoid frequent LOH allocations.&lt;/li>
&lt;li>Reuse large objects instead of allocating new ones.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Be Mindful of Pinned Objects&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Use pinning sparingly, as it can lead to memory fragmentation.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Monitor and Profile GC Performance&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Use &lt;strong>dotnet-counters&lt;/strong>, &lt;strong>dotnet-trace&lt;/strong>, or &lt;strong>PerfView&lt;/strong> to analyse GC performance and identify bottlenecks.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>The .NET 8 garbage collector is more efficient than ever, thanks to optimisations in concurrency, fragmentation management, and workload adaptability. By understanding its inner workings and applying best practices, developers can write more performant applications with minimal memory-related bottlenecks. Whether you’re building a high-throughput server, a responsive UI application, or a cloud-native service, optimising for the .NET 8 GC will help ensure smooth and efficient memory management.&lt;/p></description></item><item><title>Why C# Developers Should Also Learn Rust, and What It Can Teach Them</title><link>https://www.darrenhorrocks.co.uk/why-csharp-developers-should-learn-rust-and-what-it-can-teach-them/</link><pubDate>Sun, 19 Jan 2025 17:02:14 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/why-csharp-developers-should-learn-rust-and-what-it-can-teach-them/</guid><description>&lt;p>As a C# developer, you might wonder why you should invest time learning Rust, a systems programming language. After all, C# is a powerful, versatile, and high-level language with a vast ecosystem and a wide range of applications. However, learning Rust can expand your programming horizons and provide unique insights that will make you a better developer, regardless of your primary language. Here&amp;rsquo;s why C# developers should consider learning Rust and what they can gain from it.&lt;/p>
&lt;h3 id="understanding-ownership-and-memory-management">&lt;strong>Understanding Ownership and Memory Management&lt;/strong>&lt;/h3>
&lt;p>In C#, memory management is largely handled by the .NET runtime and its garbage collector, which simplifies development. Rust, on the other hand, introduces a novel ownership model with strict compile-time checks that ensure memory safety without relying on garbage collection.&lt;/p>
&lt;p>By learning Rust, C# developers gain a deeper appreciation for:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>How memory is allocated and deallocated.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Avoiding common pitfalls like null references and memory leaks.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Writing performance-critical code with deterministic resource management.&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>This knowledge can directly help C# developers optimise their applications by being more mindful of how objects are created, used, and disposed of, reducing pressure on the garbage collector and improving runtime performance.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Rust Code:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;span style="color:#66d9ef">let&lt;/span> s &lt;span style="color:#f92672">=&lt;/span> String::from(&lt;span style="color:#e6db74">&amp;#34;Hello, Rust!&amp;#34;&lt;/span>); &lt;span style="color:#75715e">// Ownership of the string
&lt;/span>&lt;span style="color:#75715e">&lt;/span> println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;{}&amp;#34;&lt;/span>, s);
&lt;span style="color:#75715e">// The string is automatically deallocated when it goes out of scope
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>C# Equivalent:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> System;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span> {
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main() {
&lt;span style="color:#66d9ef">string&lt;/span> s = &lt;span style="color:#e6db74">&amp;#34;Hello, C#!&amp;#34;&lt;/span>;
Console.WriteLine(s);
&lt;span style="color:#75715e">// The garbage collector will clean up the string eventually
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="writing-safer-and-more-reliable-code">&lt;strong>Writing Safer and More Reliable Code&lt;/strong>&lt;/h3>
&lt;p>Rust&amp;rsquo;s emphasis on safety goes beyond memory management. Its powerful type system and strict compiler checks help prevent issues like data races, uninitialised variables, and mismatched types at compile time.&lt;/p>
&lt;p>For C# developers, learning Rust can:&lt;/p>
&lt;ul>
&lt;li>Reinforce the habit of thinking through edge cases.&lt;/li>
&lt;li>Highlight the value of immutability and its role in creating thread-safe code.&lt;/li>
&lt;li>Encourage writing more robust code by embracing patterns that reduce runtime errors.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Rust Code:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">divide&lt;/span>(a: &lt;span style="color:#66d9ef">i32&lt;/span>, b: &lt;span style="color:#66d9ef">i32&lt;/span>) -&amp;gt; Option&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#66d9ef">i32&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> {
&lt;span style="color:#66d9ef">if&lt;/span> b &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span> {
None &lt;span style="color:#75715e">// Avoid division by zero
&lt;/span>&lt;span style="color:#75715e">&lt;/span> } &lt;span style="color:#66d9ef">else&lt;/span> {
Some(a &lt;span style="color:#f92672">/&lt;/span> b)
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>C# Equivalent:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span> {
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int?&lt;/span> Divide(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b) {
&lt;span style="color:#66d9ef">if&lt;/span> (b == &lt;span style="color:#ae81ff">0&lt;/span>) {
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>; &lt;span style="color:#75715e">// Avoid division by zero
&lt;/span>&lt;span style="color:#75715e">&lt;/span> } &lt;span style="color:#66d9ef">else&lt;/span> {
&lt;span style="color:#66d9ef">return&lt;/span> a / b;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="performance-and-low-level-systems-programming">&lt;strong>Performance and Low-Level Systems Programming&lt;/strong>&lt;/h3>
&lt;p>Rust is designed for high performance, making it ideal for systems programming, game development, and scenarios requiring fine-grained control over hardware resources. While C# is fast and efficient for many applications, Rust allows you to:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Optimise critical parts of your application&lt;/strong> where performance matters most.&lt;/li>
&lt;li>&lt;strong>Understand the hardware-level implications of your code,&lt;/strong> such as how data is laid out in memory.&lt;/li>
&lt;li>&lt;strong>Develop cross-platform libraries&lt;/strong> or tools with minimal overhead.&lt;/li>
&lt;/ul>
&lt;p>Even if you don&amp;rsquo;t use Rust directly in your projects, this knowledge can help you better understand performance bottlenecks in C# applications. For example, by learning about data-oriented design in Rust, you can improve cache locality and memory access patterns in performance-critical C# code, leading to faster execution times.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Rust Code (Efficient Loop):&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">let&lt;/span> data &lt;span style="color:#f92672">=&lt;/span> vec&lt;span style="color:#f92672">!&lt;/span>[&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">4&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span>];
&lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#f92672">&amp;amp;&lt;/span>item &lt;span style="color:#66d9ef">in&lt;/span> data.iter() {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;{}&amp;#34;&lt;/span>, item);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>C# Equivalent:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> data = &lt;span style="color:#66d9ef">new&lt;/span>[] { &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">4&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span> };
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> item &lt;span style="color:#66d9ef">in&lt;/span> data) {
Console.WriteLine(item);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="a-broader-perspective-on-concurrency">&lt;strong>A Broader Perspective on Concurrency&lt;/strong>&lt;/h3>
&lt;p>Concurrency is a common challenge in modern software development. While C# provides powerful abstractions like async/await and the Task Parallel Library, Rust takes a different approach by enforcing thread safety at compile time.&lt;/p>
&lt;p>By exploring Rust&amp;rsquo;s model for concurrency, you&amp;rsquo;ll:&lt;/p>
&lt;ul>
&lt;li>Gain new tools for reasoning about shared state.&lt;/li>
&lt;li>Learn how to write concurrent code without fear of data races.&lt;/li>
&lt;li>Appreciate how Rust&amp;rsquo;s borrow checker prevents common multithreading bugs.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Rust Code:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">use&lt;/span> std::thread;
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;span style="color:#66d9ef">let&lt;/span> data &lt;span style="color:#f92672">=&lt;/span> vec&lt;span style="color:#f92672">!&lt;/span>[&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>];
&lt;span style="color:#66d9ef">let&lt;/span> handle &lt;span style="color:#f92672">=&lt;/span> thread::spawn(&lt;span style="color:#66d9ef">move&lt;/span> &lt;span style="color:#f92672">||&lt;/span> {
&lt;span style="color:#66d9ef">for&lt;/span> i &lt;span style="color:#66d9ef">in&lt;/span> data {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;{}&amp;#34;&lt;/span>, i);
}
});
handle.join().unwrap();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>C# Equivalent:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> System;
&lt;span style="color:#66d9ef">using&lt;/span> System.Threading;
&lt;span style="color:#66d9ef">using&lt;/span> System.Threading.Tasks;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span> {
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task Main() {
&lt;span style="color:#66d9ef">var&lt;/span> data = &lt;span style="color:#66d9ef">new&lt;/span>[] { &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span> };
&lt;span style="color:#66d9ef">await&lt;/span> Task.Run(() =&amp;gt; {
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> item &lt;span style="color:#66d9ef">in&lt;/span> data) {
Console.WriteLine(item);
}
});
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="embracing-a-new-way-of-thinking">&lt;strong>Embracing a New Way of Thinking&lt;/strong>&lt;/h3>
&lt;p>Learning a language like Rust, with its unique paradigms and constraints, challenges you to think differently about programming. It encourages you to:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Be more deliberate&lt;/strong> in your coding decisions.&lt;/li>
&lt;li>&lt;strong>Appreciate the trade-offs&lt;/strong> between safety, performance, and expressiveness.&lt;/li>
&lt;li>&lt;strong>Explore functional programming concepts&lt;/strong> like pattern matching and closures, which can also be applied in C#.&lt;/li>
&lt;/ul>
&lt;p>This shift in perspective can lead to writing more efficient and elegant C# code. For instance, understanding how Rust optimises for zero-cost abstractions might inspire you to rethink how you structure performance-critical sections of your C# programs.&lt;/p>
&lt;hr>
&lt;h3 id="expanding-career-opportunities">&lt;strong>Expanding Career Opportunities&lt;/strong>&lt;/h3>
&lt;p>As Rust grows in popularity, demand for Rust developers is increasing in fields like blockchain, embedded systems, and cloud infrastructure. Adding Rust to your skill set not only broadens your technical expertise but also opens up new career paths that might not be accessible with C# alone.&lt;/p>
&lt;hr>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>Learning Rust can be a transformative experience for C# developers. It deepens your understanding of memory management, safety, performance, and concurrency while challenging you to approach problems from a new perspective. Even if you continue to primarily work in C#, the lessons you learn from Rust can make you a more thoughtful, efficient, and versatile developer.&lt;/p>
&lt;p>By applying these insights to your C# projects, you can write optimised, high-performance applications that are safer and more reliable. So, why not give Rust a try? You might discover a new way to solve problems—and in the process, elevate your skills in both languages.&lt;/p></description></item><item><title>Why Frontend Software Development Is Needlessly Complex, When It Does Not Need to Be</title><link>https://www.darrenhorrocks.co.uk/why-frontend-software-development-needlessly-complex-when-it-does-not-need-to-be/</link><pubDate>Thu, 02 Jan 2025 14:08:41 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/why-frontend-software-development-needlessly-complex-when-it-does-not-need-to-be/</guid><description>&lt;p>Frontend software development has evolved drastically over the last decade, transforming from simple static HTML pages to dynamic, interactive web applications. While the growth of the field has unlocked new possibilities, it has also introduced layers of complexity that, arguably, aren&amp;rsquo;t always necessary. The question worth asking is: why has frontend development become so convoluted, and does it really need to be this way?&lt;/p>
&lt;h4 id="the-rise-of-complexity">The Rise of Complexity&lt;/h4>
&lt;p>The world of frontend development today is a maze of frameworks, libraries, and tools. Angular, React, Vue, Svelte—each promises to simplify development, yet their adoption often comes with steep learning curves. Add to this the tooling required for bundling (Webpack, Vite, or Rollup), testing (Jest, Cypress, or Mocha), and linting (ESLint, Prettier), and it’s easy to see how a straightforward task can balloon into an overwhelming endeavor.&lt;/p>
&lt;p>The justification for this complexity often stems from the demand for highly performant, scalable, and maintainable applications. Businesses want rich user interfaces, rapid load times, and the ability to handle millions of users. While these goals are valid, they don&amp;rsquo;t always apply universally. Many applications don&amp;rsquo;t need the latest cutting-edge features, yet developers still adopt over-engineered solutions by default.&lt;/p>
&lt;h4 id="where-we-went-wrong">Where We Went Wrong&lt;/h4>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Over-Abstraction&lt;/strong>: Frameworks and libraries abstract away many of the complexities of browser behavior and DOM manipulation. While abstractions can be powerful, they often lead to situations where developers spend more time understanding the abstraction than solving actual problems.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Dependency Overload&lt;/strong>: The npm ecosystem encourages a dependency-first approach, where every problem seems to have a package. This results in bloated applications, dependency conflicts, and security vulnerabilities.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Tooling Fatigue&lt;/strong>: The sheer number of tools required to set up a modern development environment can be intimidating. Do we really need a full CI/CD pipeline, TypeScript integration, and CSS-in-JS for a simple blog?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Chasing Trends&lt;/strong>: The rapid pace of innovation in frontend development means new tools and practices emerge regularly. Developers often feel pressure to keep up with trends, leading to unnecessary rewrites and added complexity.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="what-can-be-done">What Can Be Done?&lt;/h4>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Embrace Simplicity&lt;/strong>: Not every project requires a heavyweight framework. For smaller applications, vanilla JavaScript or lightweight libraries like Alpine.js can suffice.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Right-Sizing Abstractions&lt;/strong>: Use abstractions that genuinely add value to your project. Avoid abstracting for the sake of it; focus on tools and patterns that solve real problems.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Mindful Tooling&lt;/strong>: Streamline your development workflow. Choose tools that align with your project’s needs rather than defaulting to industry standards.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Educate and Advocate&lt;/strong>: Encourage teams to question the necessity of each layer of complexity. Build a culture where it’s okay to say no to new tools or paradigms when they’re not needed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Focus on Fundamentals&lt;/strong>: Solid understanding of HTML, CSS, and JavaScript remains invaluable. Mastery of the basics often reduces the perceived need for complex frameworks.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h4 id="conclusion">Conclusion&lt;/h4>
&lt;p>Frontend development doesn’t have to be a labyrinth of complexity. By challenging the status quo, focusing on simplicity, and making intentional choices, we can build robust applications without unnecessary overhead. Let’s shift the narrative from “what’s trending?” to “what’s practical?” The result will be better software and happier developers.&lt;/p></description></item><item><title>Linux Is NOT the Developers Paradise You Have Been Told It Is</title><link>https://www.darrenhorrocks.co.uk/linux-not-developers-paradise-you-have-been-told-it/</link><pubDate>Wed, 11 Dec 2024 10:55:23 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/linux-not-developers-paradise-you-have-been-told-it/</guid><description>&lt;p>Linux is often heralded as the holy grail for developers - a flexible, open-source playground free of corporate shackles. It&amp;rsquo;s the darling of the tech-savvy, the underdog in the OS wars, and the supposed utopia for programmers everywhere. But let&amp;rsquo;s take a step back from the hype. While Linux has its merits, it&amp;rsquo;s not the flawless paradise that some people claim. Here&amp;rsquo;s why.&lt;/p>
&lt;h3 id="its-a-compatibility-nightmare">It&amp;rsquo;s a Compatibility Nightmare&lt;/h3>
&lt;p>Linux offers unparalleled customisation, but it comes at a cost - compatibility. Hardware support can be hit or miss, especially with niche devices or newer tech. Developers often find themselves scouring forums, compiling drivers, or tweaking configurations just to get basic peripherals working.&lt;/p>
&lt;p>Even on the software side, compatibility can be an issue. Need Adobe Creative Suite? Tough luck. Want to play nice with proprietary enterprise software? You&amp;rsquo;re likely stuck with virtual machines or dual-booting.&lt;/p>
&lt;h3 id="the-endless-tinkering">The Endless Tinkering&lt;/h3>
&lt;p>Linux&amp;rsquo;s endless customisation options are a double-edged sword. While some developers revel in the ability to tweak every aspect of their system, others fall into a productivity black hole. Hours that could be spent coding are instead consumed by perfecting your desktop environment, troubleshooting obscure bugs, or optimising performance for the nth time.&lt;/p>
&lt;p>For developers working on tight deadlines or in professional environments, this can quickly become a liability.&lt;/p>
&lt;h3 id="fractured-ecosystem">Fractured Ecosystem&lt;/h3>
&lt;p>One of Linux&amp;rsquo;s greatest strengths is also its biggest weakness - its diversity/fragmentation. With countless distributions (distros) to choose from, there&amp;rsquo;s a flavor of Linux for everyone. But this fragmentation leads to inconsistency.&lt;/p>
&lt;p>Developers often face compatibility issues between distros, forcing them to either standardise on one or deal with the quirks of multiple environments. Tutorials and documentation are often written for specific distros, leaving newcomers bewildered when commands or features don&amp;rsquo;t translate.&lt;/p>
&lt;h3 id="the-learning-curve">The Learning Curve&lt;/h3>
&lt;p>Linux is not inherently user-friendly, especially for developers who are new to the ecosystem. Learning the ins and outs of the command line, navigating cryptic error messages, and understanding Linux file systems can be daunting.&lt;/p>
&lt;p>For developers with limited time to spare, this steep learning curve can feel like an unnecessary hurdle, particularly when other operating systems like macOS or Windows offer more intuitive experiences.&lt;/p>
&lt;h3 id="corporate-resistance">Corporate Resistance&lt;/h3>
&lt;p>While Linux thrives in server environments, its adoption as a desktop OS in corporate settings remains limited. Developers working in enterprise environments often find themselves shackled to Windows or macOS, which offer better support for proprietary tools and enterprise-grade software.&lt;/p>
&lt;p>Even if a developer prefers Linux, the corporate world&amp;rsquo;s reliance on other operating systems often makes switching impractical.&lt;/p>
&lt;h3 id="so-what-now">So, What Now?&lt;/h3>
&lt;p>To be clear, Linux is a powerful, versatile, and exciting operating system. For the right developer - someone who values freedom, thrives on problem-solving, and isn&amp;rsquo;t afraid to get their hands dirty - it can be a dream come true. But for many others, it&amp;rsquo;s not the paradise it&amp;rsquo;s cracked up to be.&lt;/p>
&lt;p>The next time someone tells you that Linux is the only OS for serious developers, take their advice with a grain of salt. The perfect operating system doesn&amp;rsquo;t exist - it&amp;rsquo;s about finding what works best for your needs, whether that&amp;rsquo;s Linux, Windows, macOS, or even a combination of them.&lt;/p>
&lt;p>&lt;strong>What&amp;rsquo;s your take on Linux? Have you found it to be a paradise, a nightmare, or something in between? Let us know in the comments below!&lt;/strong>&lt;/p></description></item><item><title>Why Do Open Source Applications Often Have Less Polished UIs Than Commercial Software</title><link>https://www.darrenhorrocks.co.uk/why-open-source-ui-design-sucks/</link><pubDate>Thu, 05 Dec 2024 23:02:00 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/why-open-source-ui-design-sucks/</guid><description>&lt;p>Open-source applications play a critical role in technology, offering powerful tools, innovation, and community-driven development. However, many users have noticed that the user interfaces (UIs) of open-source software often lack the polish and finesse of their commercial, closed-source counterparts. This perceived gap isn’t due to a lack of talent but arises from unique challenges and priorities inherent to open-source projects.&lt;/p>
&lt;h3 id="resource-allocation-priorities-over-aesthetics">&lt;strong>Resource Allocation: Priorities Over Aesthetics&lt;/strong>&lt;/h3>
&lt;p>Open-source projects often prioritize functionality, performance, and robustness over visual design. Since most contributors are developers who are solving specific problems, their focus naturally leans towards creating software that &lt;em>works&lt;/em> rather than one that looks visually appealing.&lt;/p>
&lt;p>On the other hand, commercial software companies have dedicated budgets to hire UI/UX designers, whose primary role is to craft an attractive and intuitive user experience. In contrast, open-source projects rarely have the financial resources to prioritize such roles.&lt;/p>
&lt;h3 id="volunteer-driven-development">&lt;strong>Volunteer-Driven Development&lt;/strong>&lt;/h3>
&lt;p>Open-source projects rely heavily on volunteers. These contributors typically work on the project in their free time and focus on areas where they have expertise or personal interest. Since skilled UI/UX designers are in high demand in the commercial sector, they are less likely to volunteer their time for open-source initiatives.&lt;/p>
&lt;p>Without a dedicated team addressing the aesthetics, UI improvements are often delayed or entirely neglected in favor of core functionality.&lt;/p>
&lt;h3 id="user-base-expectations">&lt;strong>User Base Expectations&lt;/strong>&lt;/h3>
&lt;p>The user base of open-source software often consists of tech-savvy individuals, developers, and businesses that prioritize customization, control, and functionality over design. For these users, a polished UI may feel less critical than having open access to the software&amp;rsquo;s source code or ensuring compatibility with niche requirements.&lt;/p>
&lt;p>Conversely, commercial software is marketed to a broader audience, including non-technical users who place higher importance on an intuitive and visually pleasing interface.&lt;/p>
&lt;h3 id="the-challenge-of-design-consistency">&lt;strong>The Challenge of Design Consistency&lt;/strong>&lt;/h3>
&lt;p>Open-source projects frequently have many contributors with diverse visions and styles, making it difficult to maintain a consistent design language across the application. This lack of cohesion can result in interfaces that feel disjointed or unintuitive.&lt;/p>
&lt;p>Commercial software benefits from a centralized vision. Design guidelines, brand standards, and dedicated leadership ensure a uniform look and feel across every feature and update.&lt;/p>
&lt;h3 id="iterative-design-processes-in-open-source">&lt;strong>Iterative Design Processes in Open Source&lt;/strong>&lt;/h3>
&lt;p>Open-source UIs often evolve in response to user feedback, which can lead to patchwork changes rather than comprehensive redesigns. While this approach ensures practical usability, it can leave the software feeling less cohesive and modern compared to commercial applications that undergo extensive user testing and iterative design processes.&lt;/p>
&lt;h3 id="monetary-incentives-and-competitive-pressure">&lt;strong>Monetary Incentives and Competitive Pressure&lt;/strong>&lt;/h3>
&lt;p>Commercial software faces immense competition in the market, pushing companies to invest heavily in eye-catching, user-friendly interfaces to stand out. Features are often marketed through sleek, demo-ready designs that appeal to potential buyers.&lt;/p>
&lt;p>Open-source software, on the other hand, does not operate on the same profit-driven model. Its success is often measured by its adoption rate and community contributions, where design appeal plays a secondary role.&lt;/p>
&lt;h3 id="closing-the-gap">&lt;strong>Closing the Gap&lt;/strong>&lt;/h3>
&lt;p>Despite these challenges, many open-source projects are beginning to prioritize UI improvements. Projects like &lt;strong>LibreOffice&lt;/strong>, &lt;strong>GIMP&lt;/strong>, and &lt;strong>Blender&lt;/strong> have made significant strides in modernizing their interfaces, thanks to increased community awareness of the importance of design. Some open-source initiatives also fundraise to hire professional designers or offer grants to contributors with UI/UX expertise.&lt;/p>
&lt;p>Collaborations between open-source communities and design professionals could help bridge the gap, creating software that is both functionally powerful and visually appealing.&lt;/p>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>The less-polished UIs of open-source software stem from structural differences in priorities, funding, and resources compared to commercial software. However, as the importance of design continues to grow, open-source communities are finding ways to address these challenges. With time, we may see more open-source applications boasting UIs that rival their commercial counterparts, combining the best of functionality and form.&lt;/p></description></item><item><title>My Top 6 Productivity Tools as C# Developer</title><link>https://www.darrenhorrocks.co.uk/my-top-6-productivity-tools-as-c-sharp-developer/</link><pubDate>Wed, 04 Dec 2024 13:56:23 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/my-top-6-productivity-tools-as-c-sharp-developer/</guid><description>&lt;p>As a C# developer, I have a wealth of tools at my disposal, each designed to make coding, debugging, and deployment easier, or more efficient. Whether you’re new to the language, or programming in general, or even an experienced developer, leveraging these tools can significantly enhance your productivity. So these are my six essential tools every C# programmer should use:&lt;/p>
&lt;h4 id="visual-studio">&lt;strong>Visual Studio&lt;/strong>&lt;/h4>
&lt;p>&lt;em>The powerhouse for C# development.&lt;/em>&lt;/p>
&lt;p>Visual Studio is more than just an IDE—it’s a complete development environment tailored to C# and .NET developers. Its key features include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>IntelliSense:&lt;/strong> Provides intelligent code suggestions as you type, reducing errors and speeding up coding.&lt;/li>
&lt;li>&lt;strong>Built-in Debugger:&lt;/strong> An intuitive debugger with breakpoint management, variable inspection, and real-time execution flow control.&lt;/li>
&lt;li>&lt;strong>Rich Extension Ecosystem:&lt;/strong> From testing frameworks to design tools, Visual Studio supports a vast library of extensions.&lt;/li>
&lt;/ul>
&lt;p>&lt;em>For those who want a faster, more lightweight option, &lt;strong>Visual Studio Code&lt;/strong> paired with the &lt;strong>C# by OmniSharp&lt;/strong> extension offers essential functionality like debugging, linting, and IntelliSense. It’s ideal for smaller projects or developers who prefer cross-platform development.&lt;/em>&lt;/p>
&lt;h4 id="resharper">&lt;strong>ReSharper&lt;/strong>&lt;/h4>
&lt;p>&lt;em>Elevate your coding experience.&lt;/em>&lt;/p>
&lt;p>ReSharper by JetBrains is a premium tool for Visual Studio that enhances your coding workflow by analyzing your code in real time. Its features include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Code Refactoring:&lt;/strong> Automatically improve the structure of your code without altering its behavior.&lt;/li>
&lt;li>&lt;strong>Code Analysis:&lt;/strong> Detects potential issues, such as unused variables or inefficient code patterns, as you write.&lt;/li>
&lt;li>&lt;strong>Navigation Tools:&lt;/strong> Quickly locate classes, methods, or files in large projects with powerful search capabilities.&lt;/li>
&lt;/ul>
&lt;p>ReSharper’s ability to suggest improvements and automate repetitive tasks makes it invaluable for maintaining clean, efficient, and maintainable code.&lt;/p>
&lt;h4 id="linqpad">&lt;strong>LINQPad&lt;/strong>&lt;/h4>
&lt;p>&lt;em>Test and prototype with ease.&lt;/em>&lt;/p>
&lt;p>LINQPad is not just a tool for running LINQ queries—it’s an interactive coding environment for C#. Its capabilities include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Prototyping:&lt;/strong> Test small snippets of C# code without setting up a full project. This is perfect for debugging algorithms or trying out new ideas.&lt;/li>
&lt;li>&lt;strong>Database Integration:&lt;/strong> Query SQL databases with LINQ and view the results instantly.&lt;/li>
&lt;li>&lt;strong>.NET Exploration:&lt;/strong> Dive deep into the .NET Framework with examples and interactive tutorials.&lt;/li>
&lt;/ul>
&lt;p>The ability to run and test code in a standalone environment makes LINQPad a great tool for experimentation and learning.&lt;/p>
&lt;h4 id="postman">&lt;strong>Postman&lt;/strong>&lt;/h4>
&lt;p>&lt;em>Streamline API interactions.&lt;/em>&lt;/p>
&lt;p>Postman is an essential tool for developers working with APIs, which are increasingly central to modern applications. Its features are tailored to make API testing seamless:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Request Crafting:&lt;/strong> Easily create GET, POST, PUT, DELETE, and other HTTP requests with custom headers, parameters, and body content.&lt;/li>
&lt;li>&lt;strong>Response Validation:&lt;/strong> Check the responses from APIs to ensure they meet your application&amp;rsquo;s requirements.&lt;/li>
&lt;li>&lt;strong>Automation:&lt;/strong> Save collections of API calls, automate tests, and even integrate them into CI/CD pipelines for ongoing validation.&lt;/li>
&lt;/ul>
&lt;p>Postman is particularly useful for C# developers building web applications or consuming third-party APIs, as it ensures smooth integration and functionality.&lt;/p>
&lt;h4 id="fiddler">&lt;strong>Fiddler&lt;/strong>&lt;/h4>
&lt;p>&lt;em>Debugging network traffic made easy.&lt;/em>&lt;/p>
&lt;p>When building web applications or services, understanding and troubleshooting network requests is crucial. Fiddler is a powerful tool for inspecting network activity:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Traffic Monitoring:&lt;/strong> View all HTTP and HTTPS traffic between your application and servers.&lt;/li>
&lt;li>&lt;strong>Request Manipulation:&lt;/strong> Edit and replay requests to test how your application responds under different scenarios.&lt;/li>
&lt;li>&lt;strong>Performance Analysis:&lt;/strong> Identify and resolve latency issues, bottlenecks, or misconfigured requests.&lt;/li>
&lt;/ul>
&lt;p>Fiddler is especially valuable for diagnosing connectivity issues in APIs or web services and ensuring your applications communicate effectively with external systems.&lt;/p>
&lt;h4 id="azure-devops">&lt;strong>Azure DevOps&lt;/strong>&lt;/h4>
&lt;p>&lt;em>Manage projects and streamline deployment.&lt;/em>&lt;/p>
&lt;p>Azure DevOps is a comprehensive suite of tools for planning, developing, and deploying applications. C# developers benefit from its seamless integration with the .NET ecosystem. Key features include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Work Management:&lt;/strong> Use Kanban boards, backlogs, and work items to plan and track development tasks.&lt;/li>
&lt;li>&lt;strong>Build and Release Pipelines:&lt;/strong> Automate building, testing, and deploying your C# applications across environments.&lt;/li>
&lt;li>&lt;strong>Collaboration Tools:&lt;/strong> Integrate code repositories, manage pull requests, and collaborate with your team efficiently.&lt;/li>
&lt;/ul>
&lt;p>Azure DevOps empowers teams to adopt DevOps practices, improving the overall development lifecycle from coding to deployment.&lt;/p>
&lt;h3 id="honorable-mentions">Honorable Mentions&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Dapper:&lt;/strong> A lightweight ORM for fast and efficient database queries, especially useful for developers seeking alternatives to Entity Framework.&lt;/li>
&lt;li>&lt;strong>Rider:&lt;/strong> JetBrains’ cross-platform IDE for .NET development, offering performance comparable to Visual Studio with a fresh user interface.&lt;/li>
&lt;li>&lt;strong>Notepad++ or Sublime Text:&lt;/strong> Simple yet powerful text editors ideal for quick edits or reviewing code outside the IDE.&lt;/li>
&lt;/ul>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>C# programmers have access to a variety of tools that cater to every stage of the development lifecycle. From coding and debugging to testing and deployment, integrating these tools into your workflow can significantly boost productivity.&lt;/p>
&lt;p>Have a favorite tool that didn’t make the list? Share your recommendations and tips to help other C# developers level up their workflows!&lt;/p>
&lt;p>What would you have added to this list, and why?&lt;/p></description></item><item><title>Why C# Should Be Your First Language and is the Best Programming Language for Beginners</title><link>https://www.darrenhorrocks.co.uk/why-c-sharp-should-be-your-first-language/</link><pubDate>Tue, 03 Dec 2024 23:41:41 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/why-c-sharp-should-be-your-first-language/</guid><description>&lt;p>When choosing your first programming language, the decision can feel overwhelming. With so many languages available, each with unique strengths, you might wonder, &lt;em>Where should I start?&lt;/em> Enter &lt;strong>C#&lt;/strong>—a powerful, versatile, and beginner-friendly language. In this article, we&amp;rsquo;ll explore why C# stands out as the best choice for beginners, backed by its features, community, and versatility.&lt;/p>
&lt;h3 id="c-is-a-c-based-language">&lt;strong>C# is a C-Based Language&lt;/strong>&lt;/h3>
&lt;p>C# has its roots in the C family of programming languages, which includes heavyweights like C, C++, and Java. This lineage gives it a syntax that&amp;rsquo;s familiar and widely adopted.&lt;/p>
&lt;p>Learning C# provides an excellent foundation for transitioning to other C-based languages in the future. Concepts like loops, conditionals, and object-oriented programming (OOP) are implemented in a way that closely mirrors other popular languages, making it easier to pick up Java, JavaScript, or even C++ later in your journey.&lt;/p>
&lt;h3 id="a-large-and-supportive-community">&lt;strong>A Large and Supportive Community&lt;/strong>&lt;/h3>
&lt;p>One of the most significant hurdles for beginners is troubleshooting issues and finding learning resources. Thankfully, C# boasts a &lt;strong>large and active community&lt;/strong>.&lt;/p>
&lt;p>Whether you’re seeking help on forums like Stack Overflow, diving into tutorials on YouTube, or exploring documentation, there&amp;rsquo;s no shortage of guidance. Microsoft&amp;rsquo;s official resources, coupled with open-source communities, ensure that answers are never far away.&lt;/p>
&lt;h3 id="open-source-and-free-to-use">&lt;strong>Open Source and Free to Use&lt;/strong>&lt;/h3>
&lt;p>C# is part of the open-source &lt;strong>.NET ecosystem&lt;/strong>, which means the language, tools, and frameworks are freely available. For a beginner, this removes the barrier of expensive licenses or proprietary restrictions. You can write, debug, and deploy your first application without spending a dime.&lt;/p>
&lt;h3 id="cross-platform-development">&lt;strong>Cross-Platform Development&lt;/strong>&lt;/h3>
&lt;p>With the introduction of &lt;strong>.NET Core&lt;/strong>, C# has become a cross-platform powerhouse. You can develop applications that run seamlessly on Windows, macOS, and Linux. This versatility is invaluable in today’s world, where software often needs to work across diverse environments. For beginners, this means you can experiment and deploy projects without worrying about platform constraints.&lt;/p>
&lt;h3 id="mature-and-beginner-friendly-toolset">&lt;strong>Mature and Beginner-Friendly Toolset&lt;/strong>&lt;/h3>
&lt;p>C# is backed by a mature and polished ecosystem of tools, most notably &lt;strong>Visual Studio&lt;/strong> and &lt;strong>Visual Studio Code&lt;/strong>. These integrated development environments (IDEs) offer powerful features like:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>IntelliSense&lt;/strong>: Real-time code suggestions that help you write error-free code faster.&lt;/li>
&lt;li>&lt;strong>Debugging Tools&lt;/strong>: Step through your code line by line to identify and fix issues.&lt;/li>
&lt;li>&lt;strong>Templates&lt;/strong>: Ready-made project structures to kickstart your learning.&lt;/li>
&lt;/ul>
&lt;p>These tools reduce the steepness of the learning curve, helping you focus on understanding programming concepts without being bogged down by setup complexities.&lt;/p>
&lt;h3 id="object-oriented-programming-oop">&lt;strong>Object-Oriented Programming (OOP)&lt;/strong>&lt;/h3>
&lt;p>C# is built with &lt;strong>object-oriented principles&lt;/strong> at its core. OOP is a paradigm that&amp;rsquo;s easy to understand and widely used across the industry. By learning C#, you gain a solid grasp of concepts like:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Classes and Objects&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Inheritance&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Encapsulation&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Polymorphism&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>These principles are transferable to other programming languages and essential for tackling complex projects.&lt;/p>
&lt;h3 id="versatility-across-applications">&lt;strong>Versatility Across Applications&lt;/strong>&lt;/h3>
&lt;p>C# isn&amp;rsquo;t just for one type of application. It powers:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Desktop Apps&lt;/strong> (e.g., Windows Forms, WPF)&lt;/li>
&lt;li>&lt;strong>Web Apps&lt;/strong> (e.g., ASP.NET)&lt;/li>
&lt;li>&lt;strong>Game Development&lt;/strong> (e.g., Unity Engine)&lt;/li>
&lt;li>&lt;strong>Mobile Apps&lt;/strong> (e.g., Xamarin and .NET MAUI)&lt;/li>
&lt;li>&lt;strong>Cloud Services&lt;/strong> (e.g., Azure integration)&lt;/li>
&lt;/ul>
&lt;p>This versatility means you can explore various domains as you grow, from game development to web design, all with the same language.&lt;/p>
&lt;h3 id="strong-career-opportunities">&lt;strong>Strong Career Opportunities&lt;/strong>&lt;/h3>
&lt;p>C# is in high demand across industries. By learning C#, you&amp;rsquo;re not just acquiring a skill for hobby projects; you&amp;rsquo;re also building a foundation for a lucrative and stable career. Major companies like Microsoft, Unity, and Stack Overflow rely heavily on C#, ensuring its relevance for years to come.&lt;/p>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>C# strikes the perfect balance for beginners: it’s accessible yet powerful, versatile yet structured. Its C-based syntax, thriving community, open-source nature, cross-platform support, and robust toolset make it an ideal choice for anyone starting their programming journey.&lt;/p>
&lt;p>By choosing C#, you’re not just learning a language—you’re stepping into an ecosystem that will grow with you, offering endless opportunities for personal and professional development. So, fire up your IDE and start coding; the world of C# awaits!&lt;/p></description></item><item><title>How To: Create a Basic GraphQL API with dotnet in C#</title><link>https://www.darrenhorrocks.co.uk/how-to-create-basic-graphql-api-with-dotnet-c-sharp/</link><pubDate>Sat, 09 Nov 2024 19:10:09 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/how-to-create-basic-graphql-api-with-dotnet-c-sharp/</guid><description>&lt;p>GraphQL is better than OData, and REST&amp;hellip; So this guide will show you how to create a GraphQL API allows you to offer a flexible data querying interface where clients can request exactly the data they need. Here, we’ll create a simple GraphQL API in C# that returns data about music artists and their albums.&lt;/p>
&lt;h3 id="prerequisites">Prerequisites&lt;/h3>
&lt;p>To follow along, you need to make sure that you have:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>.NET SDK&lt;/strong> (version 5 or later)&lt;/li>
&lt;li>Familiarity with &lt;strong>ASP.NET Core&lt;/strong>&lt;/li>
&lt;li>&lt;strong>GraphQL for .NET library&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>You can download the .NET SDK from &lt;a href="https://dotnet.microsoft.com/download">the .NET website&lt;/a>.&lt;/p>
&lt;h3 id="set-up-your-project">Set Up Your Project&lt;/h3>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Create a new ASP.NET Core Web API project&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">dotnet new webapi -n GraphQLMusicApi
cd GraphQLMusicApi
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Add the GraphQL.NET package&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">dotnet add package GraphQL --version 4.6.1
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Add ASP.NET Core GraphQL integration&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">dotnet add package GraphQL.Server.Transports.AspNetCore --version 5.0.0
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="define-your-data-models">Define Your Data Models&lt;/h3>
&lt;p>For this example, we’ll create two simple models: &lt;code>Artist&lt;/code> and &lt;code>Album&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Artist&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> List&amp;lt;Album&amp;gt; Albums { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Album&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Title { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Year { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, we’ll create a data repository to act as our data source.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MusicRepository&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> List&amp;lt;Artist&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>artists = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;Artist&amp;gt;
{
&lt;span style="color:#66d9ef">new&lt;/span> Artist {
Id = &lt;span style="color:#ae81ff">1&lt;/span>,
Name = &lt;span style="color:#e6db74">&amp;#34;The Beatles&amp;#34;&lt;/span>,
Albums = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;Album&amp;gt;
{
&lt;span style="color:#66d9ef">new&lt;/span> Album { Id = &lt;span style="color:#ae81ff">1&lt;/span>, Title = &lt;span style="color:#e6db74">&amp;#34;Abbey Road&amp;#34;&lt;/span>, Year = &lt;span style="color:#ae81ff">1969&lt;/span> },
&lt;span style="color:#66d9ef">new&lt;/span> Album { Id = &lt;span style="color:#ae81ff">2&lt;/span>, Title = &lt;span style="color:#e6db74">&amp;#34;Let It Be&amp;#34;&lt;/span>, Year = &lt;span style="color:#ae81ff">1970&lt;/span> }
}
},
&lt;span style="color:#66d9ef">new&lt;/span> Artist {
Id = &lt;span style="color:#ae81ff">2&lt;/span>,
Name = &lt;span style="color:#e6db74">&amp;#34;Pink Floyd&amp;#34;&lt;/span>,
Albums = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;Album&amp;gt;
{
&lt;span style="color:#66d9ef">new&lt;/span> Album { Id = &lt;span style="color:#ae81ff">3&lt;/span>, Title = &lt;span style="color:#e6db74">&amp;#34;The Dark Side of the Moon&amp;#34;&lt;/span>, Year = &lt;span style="color:#ae81ff">1973&lt;/span> },
&lt;span style="color:#66d9ef">new&lt;/span> Album { Id = &lt;span style="color:#ae81ff">4&lt;/span>, Title = &lt;span style="color:#e6db74">&amp;#34;Wish You Were Here&amp;#34;&lt;/span>, Year = &lt;span style="color:#ae81ff">1975&lt;/span> }
}
}
};
&lt;span style="color:#66d9ef">public&lt;/span> IEnumerable&amp;lt;Artist&amp;gt; GetAllArtists() =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>artists;
&lt;span style="color:#66d9ef">public&lt;/span> Artist GetArtistById(&lt;span style="color:#66d9ef">int&lt;/span> id) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>artists.FirstOrDefault(a =&amp;gt; a.Id == id);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="define-the-graphql-schema">Define the GraphQL Schema&lt;/h3>
&lt;p>With GraphQL, we need to define types and queries for our models.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Create the Album GraphQL Type&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> GraphQL.Types;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">AlbumType&lt;/span> : ObjectGraphType&amp;lt;Album&amp;gt;
{
&lt;span style="color:#66d9ef">public&lt;/span> AlbumType()
{
Field(x =&amp;gt; x.Id).Description(&lt;span style="color:#e6db74">&amp;#34;The ID of the album.&amp;#34;&lt;/span>);
Field(x =&amp;gt; x.Title).Description(&lt;span style="color:#e6db74">&amp;#34;The title of the album.&amp;#34;&lt;/span>);
Field(x =&amp;gt; x.Year).Description(&lt;span style="color:#e6db74">&amp;#34;The release year of the album.&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Create the Artist GraphQL Type&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ArtistType&lt;/span> : ObjectGraphType&amp;lt;Artist&amp;gt;
{
&lt;span style="color:#66d9ef">public&lt;/span> ArtistType()
{
Field(x =&amp;gt; x.Id).Description(&lt;span style="color:#e6db74">&amp;#34;The ID of the artist.&amp;#34;&lt;/span>);
Field(x =&amp;gt; x.Name).Description(&lt;span style="color:#e6db74">&amp;#34;The name of the artist.&amp;#34;&lt;/span>);
Field&amp;lt;ListGraphType&amp;lt;AlbumType&amp;gt;&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;albums&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;The albums by the artist&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Define the Query Class&lt;/strong>:&lt;/p>
&lt;p>This class defines the queries for retrieving artists and albums.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MusicQuery&lt;/span> : ObjectGraphType
{
&lt;span style="color:#66d9ef">public&lt;/span> MusicQuery(MusicRepository repository)
{
Field&amp;lt;ListGraphType&amp;lt;ArtistType&amp;gt;&amp;gt;(
&lt;span style="color:#e6db74">&amp;#34;artists&amp;#34;&lt;/span>,
resolve: context =&amp;gt; repository.GetAllArtists()
);
Field&amp;lt;ArtistType&amp;gt;(
&lt;span style="color:#e6db74">&amp;#34;artist&amp;#34;&lt;/span>,
arguments: &lt;span style="color:#66d9ef">new&lt;/span> QueryArguments(&lt;span style="color:#66d9ef">new&lt;/span> QueryArgument&amp;lt;IntGraphType&amp;gt; { Name = &lt;span style="color:#e6db74">&amp;#34;id&amp;#34;&lt;/span> }),
resolve: context =&amp;gt;
{
&lt;span style="color:#66d9ef">var&lt;/span> id = context.GetArgument&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;id&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> repository.GetArtistById(id);
}
);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Create the Schema&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MusicSchema&lt;/span> : Schema
{
&lt;span style="color:#66d9ef">public&lt;/span> MusicSchema(IServiceProvider provider) : &lt;span style="color:#66d9ef">base&lt;/span>(provider)
{
Query = provider.GetRequiredService&amp;lt;MusicQuery&amp;gt;();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="set-up-dependency-injection-and-middleware">Set Up Dependency Injection and Middleware&lt;/h3>
&lt;p>To use these services, add them to the service collection and configure middleware in &lt;code>Startup.cs&lt;/code>.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Configure Services&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> ConfigureServices(IServiceCollection services)
{
services.AddSingleton&amp;lt;MusicRepository&amp;gt;();
services.AddSingleton&amp;lt;AlbumType&amp;gt;();
services.AddSingleton&amp;lt;ArtistType&amp;gt;();
services.AddSingleton&amp;lt;MusicQuery&amp;gt;();
services.AddSingleton&amp;lt;ISchema, MusicSchema&amp;gt;();
services.AddGraphQL(options =&amp;gt;
{
options.EnableMetrics = &lt;span style="color:#66d9ef">false&lt;/span>;
}).AddSystemTextJson();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Configure Middleware&lt;/strong>:&lt;/p>
&lt;p>In the &lt;code>Configure&lt;/code> method, set up the GraphQL endpoint:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoints =&amp;gt;
{
endpoints.MapGraphQL(&lt;span style="color:#e6db74">&amp;#34;/graphql&amp;#34;&lt;/span>);
});
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="test-your-api">Test Your API&lt;/h3>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Run your application&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">dotnet run
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>Navigate to&lt;/strong> &lt;code>http://localhost:5000/graphql&lt;/code>. You can test the API using a tool like GraphiQL or Postman.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Example Queries&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>To get all artists and their albums:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">&lt;span style="color:#66d9ef">query&lt;/span> {
&lt;span style="color:#a6e22e">artists&lt;/span> {
id
name
albums {
title
year
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>To get a specific artist by ID:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">&lt;span style="color:#66d9ef">query&lt;/span> {
&lt;span style="color:#a6e22e">artist&lt;/span>(id: &lt;span style="color:#a6e22e">1&lt;/span>) {
name
albums {
title
year
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="wrapping-up">Wrapping Up&lt;/h3>
&lt;p>With just a few steps, you’ve created a GraphQL API for music artists and albums. This basic API can be expanded with more complex types, queries, and mutations as needed, offering powerful data flexibility for your clients. Happy coding!&lt;/p></description></item><item><title>SOLID Design Principles Rust (with examples)</title><link>https://www.darrenhorrocks.co.uk/solid-principles-rust-with-examples/</link><pubDate>Fri, 25 Oct 2024 10:31:56 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/solid-principles-rust-with-examples/</guid><description>&lt;p>When it comes to designing software, the SOLID principles are the gold standard for object-oriented programming. While Rust isn’t an object-oriented language in the strictest sense, it offers plenty of tools to apply SOLID principles effectively. With enums, traits, and strict type safety, Rust provides a solid foundation for building flexible, maintainable, and robust applications that adhere to these design principles.&lt;/p>
&lt;h4 id="single-responsibility-principle-srp">&lt;strong>Single Responsibility Principle (SRP)&lt;/strong>&lt;/h4>
&lt;p>The Single Responsibility Principle states that a struct or function should have one, and only one, reason to change. In Rust, this translates into modules, structs, and functions that each handle a single task.&lt;/p>
&lt;p>For example, consider a simple logging system:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#75715e">// Logger only handles logging.
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">Logger&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Logger {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">log&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, message: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Log: {}&amp;#34;&lt;/span>, message);
}
}
&lt;span style="color:#75715e">// FileProcessor is responsible for file processing only.
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">FileProcessor&lt;/span> {
logger: &lt;span style="color:#a6e22e">Logger&lt;/span>,
}
&lt;span style="color:#66d9ef">impl&lt;/span> FileProcessor {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">process_file&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, filename: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>) {
&lt;span style="color:#75715e">// Process the file (e.g., reading content)
&lt;/span>&lt;span style="color:#75715e">&lt;/span> self.logger.log(&lt;span style="color:#e6db74">&amp;#34;Processing file...&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, &lt;code>Logger&lt;/code> handles logging, and &lt;code>FileProcessor&lt;/code> processes files. By separating these responsibilities, we make each component independently modifiable and testable.&lt;/p>
&lt;hr>
&lt;h4 id="openclosed-principle-ocp">&lt;strong>Open/Closed Principle (OCP)&lt;/strong>&lt;/h4>
&lt;p>The Open/Closed Principle states that modules should be open for extension but closed for modification. In Rust, this often involves using enums or trait-based polymorphism.&lt;/p>
&lt;p>Consider a payment system with multiple payment methods:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">trait&lt;/span> Payment {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">pay&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, amount: &lt;span style="color:#66d9ef">f32&lt;/span>);
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">CreditCard&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Payment &lt;span style="color:#66d9ef">for&lt;/span> CreditCard {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">pay&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, amount: &lt;span style="color:#66d9ef">f32&lt;/span>) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Paid ${} with credit card.&amp;#34;&lt;/span>, amount);
}
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">Paypal&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Payment &lt;span style="color:#66d9ef">for&lt;/span> Paypal {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">pay&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, amount: &lt;span style="color:#66d9ef">f32&lt;/span>) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Paid ${} using PayPal.&amp;#34;&lt;/span>, amount);
}
}
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">process_payment&lt;/span>(payment: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">dyn&lt;/span> Payment, amount: &lt;span style="color:#66d9ef">f32&lt;/span>) {
payment.pay(amount);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, &lt;code>Payment&lt;/code> is a trait that new payment methods can implement without modifying existing code. By adhering to OCP, we make it easy to add new payment methods without disrupting the existing &lt;code>process_payment&lt;/code> logic.&lt;/p>
&lt;hr>
&lt;h4 id="liskov-substitution-principle-lsp">&lt;strong>Liskov Substitution Principle (LSP)&lt;/strong>&lt;/h4>
&lt;p>The Liskov Substitution Principle asserts that objects of a superclass should be replaceable with objects of a subclass without altering the correctness of the program. In Rust, this is less about inheritance and more about implementing traits in a consistent way.&lt;/p>
&lt;p>Imagine an example of drawing shapes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">trait&lt;/span> Drawable {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">draw&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self);
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">Circle&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Drawable &lt;span style="color:#66d9ef">for&lt;/span> Circle {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">draw&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Drawing a circle.&amp;#34;&lt;/span>);
}
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">Square&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Drawable &lt;span style="color:#66d9ef">for&lt;/span> Square {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">draw&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Drawing a square.&amp;#34;&lt;/span>);
}
}
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">render_shape&lt;/span>(shape: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">dyn&lt;/span> Drawable) {
shape.draw();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Both &lt;code>Circle&lt;/code> and &lt;code>Square&lt;/code> implement the &lt;code>Drawable&lt;/code> trait, making them interchangeable in &lt;code>render_shape&lt;/code>. Thus, we follow the LSP by ensuring both types can replace each other in code that uses &lt;code>Drawable&lt;/code>.&lt;/p>
&lt;hr>
&lt;h4 id="interface-segregation-principle-isp">&lt;strong>Interface Segregation Principle (ISP)&lt;/strong>&lt;/h4>
&lt;p>The Interface Segregation Principle says that clients should not be forced to depend on interfaces they don’t use. In Rust, this is typically achieved by using smaller, focused traits.&lt;/p>
&lt;p>Consider a printer and scanner:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">trait&lt;/span> Printer {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">print&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, document: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>);
}
&lt;span style="color:#66d9ef">trait&lt;/span> Scanner {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">scan&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self) -&amp;gt; String;
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">AllInOneDevice&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Printer &lt;span style="color:#66d9ef">for&lt;/span> AllInOneDevice {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">print&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, document: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Printing: {}&amp;#34;&lt;/span>, document);
}
}
&lt;span style="color:#66d9ef">impl&lt;/span> Scanner &lt;span style="color:#66d9ef">for&lt;/span> AllInOneDevice {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">scan&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self) -&amp;gt; String {
&lt;span style="color:#e6db74">&amp;#34;Scanned document content&amp;#34;&lt;/span>.to_string()
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, we separate the &lt;code>Printer&lt;/code> and &lt;code>Scanner&lt;/code> traits, allowing devices that only print or scan to implement only what they need. This keeps the interface minimal and clean, aligning with the ISP.&lt;/p>
&lt;hr>
&lt;h4 id="dependency-inversion-principle-dip">&lt;strong>Dependency Inversion Principle (DIP)&lt;/strong>&lt;/h4>
&lt;p>The Dependency Inversion Principle promotes depending on abstractions, not concretions. In Rust, traits provide a great way to inject dependencies and reduce coupling between modules.&lt;/p>
&lt;p>Consider a notification system where we can notify users via email or SMS:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">trait&lt;/span> Notifier {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">send&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, message: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>);
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">EmailNotifier&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Notifier &lt;span style="color:#66d9ef">for&lt;/span> EmailNotifier {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">send&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, message: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Sending email: {}&amp;#34;&lt;/span>, message);
}
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">SmsNotifier&lt;/span>;
&lt;span style="color:#66d9ef">impl&lt;/span> Notifier &lt;span style="color:#66d9ef">for&lt;/span> SmsNotifier {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">send&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, message: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>) {
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Sending SMS: {}&amp;#34;&lt;/span>, message);
}
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">UserNotifier&lt;/span>&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#a6e22e">&amp;#39;a&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> {
notifier: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">&amp;#39;a&lt;/span> dyn Notifier,
}
&lt;span style="color:#66d9ef">impl&lt;/span>&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#a6e22e">&amp;#39;a&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> UserNotifier&lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#a6e22e">&amp;#39;a&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> {
&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">notify_user&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>self, message: &lt;span style="color:#66d9ef">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">str&lt;/span>) {
self.notifier.send(message);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>By using a trait (&lt;code>Notifier&lt;/code>) as an abstraction, we can easily inject any type of notifier (&lt;code>EmailNotifier&lt;/code>, &lt;code>SmsNotifier&lt;/code>) into &lt;code>UserNotifier&lt;/code>. This flexibility makes the code easier to test and extend.&lt;/p></description></item><item><title>Why Rust and Its Memory Safety Lulls Developers Into a False Sense of Security, Leading to More Serious Bugs</title><link>https://www.darrenhorrocks.co.uk/why-rust-its-memory-safety-lulls-developers-into-false-sense-security/</link><pubDate>Wed, 02 Oct 2024 23:49:04 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-rust-its-memory-safety-lulls-developers-into-false-sense-security/</guid><description>&lt;p>Rust has garnered significant attention in the programming world for its focus on memory safety without the need for a garbage collector, making it an appealing option for systems programming, where safety and performance are both critical. By preventing entire categories of bugs like data races, buffer overflows, and null pointer dereferencing at compile time, Rust has earned a reputation as a &amp;ldquo;safe&amp;rdquo; language. However, this very focus on memory safety can lull developers into a false sense of security, leading them to overlook other kinds of programming errors, potentially causing even more serious bugs. Here’s why.&lt;/p>
&lt;h2 id="the-memory-safety-halo-effect">&lt;strong>The Memory Safety Halo Effect&lt;/strong>&lt;/h2>
&lt;p>Rust’s strong emphasis on memory safety creates a psychological effect on developers, often leading them to assume that because the compiler ensures memory is safely managed, other aspects of their code are automatically more secure or reliable. This &amp;ldquo;halo effect&amp;rdquo; can lead developers to lower their guard when it comes to other critical aspects of software engineering, such as concurrency, logic errors, or mismanaging external resources like file systems and databases.&lt;/p>
&lt;p>While Rust can prevent certain classes of memory-related bugs, it cannot prevent logical errors, race conditions in non-shared state, or incorrect API usage. Developers who become over-reliant on the compiler’s safety checks may inadvertently write poorly architected or buggy code, believing that the language&amp;rsquo;s safety features will catch more than they actually do.&lt;/p>
&lt;h2 id="the-complexity-of-the-borrow-checker-can-encourage-riskier-designs">&lt;strong>The Complexity of the Borrow Checker Can Encourage Riskier Designs&lt;/strong>&lt;/h2>
&lt;p>Rust’s borrow checker is at the core of its memory safety model, ensuring that variables have a clear ownership model. However, the complexity of managing ownership, borrowing, and lifetimes can lead to situations where developers &amp;ldquo;fight the borrow checker,&amp;rdquo; making design compromises just to satisfy the compiler, even when those choices aren’t ideal for the problem at hand.&lt;/p>
&lt;p>In some cases, developers may opt to use &lt;code>unsafe&lt;/code> blocks—portions of code where the compiler’s memory checks are explicitly disabled—in an effort to work around borrow checker limitations. This, ironically, opens up the very memory safety vulnerabilities Rust is meant to prevent. Overuse or improper use of &lt;code>unsafe&lt;/code> can introduce subtle, hard-to-diagnose bugs that would not exist if the developer had chosen a simpler language without Rust&amp;rsquo;s safety guarantees.&lt;/p>
&lt;h2 id="overconfidence-in-safe-apis">&lt;strong>Overconfidence in Safe APIs&lt;/strong>&lt;/h2>
&lt;p>Rust&amp;rsquo;s ecosystem provides many &amp;ldquo;safe&amp;rdquo; APIs that abstract away complexity and ensure memory safety, but using them incorrectly can still lead to serious issues. For instance, using concurrency libraries like &lt;code>async&lt;/code> and &lt;code>await&lt;/code> can lead to deadlocks, race conditions, or resource contention if not used with care. Rust cannot prevent you from misusing these abstractions, and the safety checks at compile time don’t address the runtime issues that come with distributed systems or multithreading.&lt;/p>
&lt;p>Developers may assume that because Rust promotes memory safety, the libraries or patterns they use are inherently safe from a broader software engineering standpoint. This overconfidence can lead to critical oversights, where the developer fails to consider edge cases, performance pitfalls, or non-memory-related errors like invalid data processing or incomplete error handling.&lt;/p>
&lt;h2 id="increased-cognitive-load-leading-to-mistakes-elsewhere">&lt;strong>Increased Cognitive Load Leading to Mistakes Elsewhere&lt;/strong>&lt;/h2>
&lt;p>While Rust does an excellent job of enforcing safe memory access, managing ownership, borrowing, and lifetimes requires a steep learning curve and significant cognitive overhead. Developers may spend more mental energy wrestling with the compiler to satisfy memory safety constraints, leaving them with less bandwidth to focus on the actual problem their program is meant to solve.&lt;/p>
&lt;p>This &amp;ldquo;cognitive tax&amp;rdquo; can lead to overlooked errors in higher-level logic, inadequate testing, or poor documentation. While memory is safe, the business logic of the application or the correctness of algorithms might suffer as a result of a developer’s attention being disproportionately absorbed by satisfying Rust’s strict compiler checks.&lt;/p>
&lt;h2 id="misconceptions-about-what-rust-guarantees">&lt;strong>Misconceptions About What Rust Guarantees&lt;/strong>&lt;/h2>
&lt;p>Rust provides safety guarantees within the boundaries of its memory model, but those guarantees do not extend to the entire software stack. Many developers mistakenly believe that Rust&amp;rsquo;s guarantees extend to things like thread safety in all cases, deadlock prevention, or performance predictability. While Rust does help eliminate certain classes of bugs, it does not absolve developers from understanding concurrency issues, ensuring algorithmic efficiency, or mitigating performance bottlenecks in low-level systems.&lt;/p>
&lt;p>When developers take for granted that Rust handles &amp;ldquo;all the hard stuff,&amp;rdquo; they may neglect performance tuning or fail to account for how their application behaves under load, leading to inefficiencies or runtime failures that Rust’s memory safety guarantees do not address.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Rust’s memory safety features are a double-edged sword. While they provide a strong safety net against memory-related bugs, they can also lull developers into a false sense of security that may cause them to overlook other, equally important aspects of programming. In many cases, this can lead to more serious or subtle bugs than one might encounter in languages that require more explicit management of memory but don’t impose the same cognitive overhead.&lt;/p>
&lt;p>Ultimately, while Rust prevents many memory-related errors, it is not a magic bullet. Developers need to be aware of the language’s limitations, stay vigilant against overconfidence, and remember that writing safe and robust code requires more than just a good compiler—it requires careful thought and a comprehensive approach to software design.&lt;/p></description></item><item><title>Stop Designing Your Web Application for Millions of Users When You Don’t Even Have 100</title><link>https://www.darrenhorrocks.co.uk/stop-designing-web-applications-for-millions/</link><pubDate>Sun, 15 Sep 2024 22:05:07 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/stop-designing-web-applications-for-millions/</guid><description>&lt;p>It’s easy to get carried away when you’re building a new web app. You’ve got big ideas, you picture millions of users flocking to your platform, and you start imagining the kind of infrastructure needed to handle all that traffic. So, you build for scale from day one—optimising databases, setting up powerful servers, and ensuring everything is robust enough for massive growth.&lt;/p>
&lt;p>But hold on a second. If you don’t even have 100 users yet, designing your app for millions is a massive overreach. In fact, it’s probably holding you back more than helping. Here’s why you should focus on the here and now, and stop worrying about a future that may be a long way off.&lt;/p>
&lt;h3 id="you-dont-actually-know-what-you-need-yet">&lt;strong>You Don’t Actually Know What You Need Yet&lt;/strong>&lt;/h3>
&lt;p>When you&amp;rsquo;re just starting out, you’re making a lot of guesses. You &lt;em>think&lt;/em> you know what your users will want and how they&amp;rsquo;ll use your app, but the reality might be completely different. If you design your app to handle millions of users before you&amp;rsquo;ve even got your first 100, you&amp;rsquo;re making decisions based on assumptions rather than real data.&lt;/p>
&lt;p>Your priority should be to build something people want to use. Once you’ve got a solid user base, you&amp;rsquo;ll have a much better idea of what kind of scaling you&amp;rsquo;ll actually need. For now, focus on building a product that meets the needs of your first users.&lt;/p>
&lt;h3 id="over-engineering-wastes-precious-time">&lt;strong>Over-Engineering Wastes Precious Time&lt;/strong>&lt;/h3>
&lt;p>In the early days, time is your most valuable resource. Spending weeks—or even months—perfecting infrastructure that won’t be needed for a while is time you&amp;rsquo;re not spending on more important things, like building features, improving the user experience, and getting feedback.&lt;/p>
&lt;p>You don’t need a super-scalable solution from day one. You need something that works well for the users you have right now. You can always optimise for scale later on, but right now your focus should be on delivering value quickly.&lt;/p>
&lt;h3 id="scaling-early-means-unnecessary-costs">&lt;strong>Scaling Early Means Unnecessary Costs&lt;/strong>&lt;/h3>
&lt;p>Designing your app for millions of users is not just time-consuming, it’s also expensive. Building infrastructure for scale means investing in servers, databases, and cloud services that you don’t really need yet. These costs can quickly add up, and they’re not likely to pay off anytime soon if your user base is still in the double digits.&lt;/p>
&lt;p>Wouldn’t that money be better spent on marketing, product development, or customer acquisition? Scaling infrastructure can wait until your user numbers justify it.&lt;/p>
&lt;h3 id="speed-and-flexibility-are-key">&lt;strong>Speed and Flexibility Are Key&lt;/strong>&lt;/h3>
&lt;p>At this stage, what you need most is the ability to iterate quickly. You’re still figuring out exactly what your users want, and you’ll probably need to make lots of changes along the way. If you’ve built a complex, scalable system, even small changes can become a headache.&lt;/p>
&lt;p>Keep things simple so that when you get feedback from users, you can adjust quickly. Flexibility is crucial in the early stages, and over-engineering makes you less nimble.&lt;/p>
&lt;h3 id="your-future-problems-will-be-different">&lt;strong>Your Future Problems Will Be Different&lt;/strong>&lt;/h3>
&lt;p>Even if you hit the jackpot and end up with millions of users, the challenges you face at that point will be very different from the ones you&amp;rsquo;re imagining now. The way your app works today will likely evolve, and you won’t really know what those future challenges look like until you get there.&lt;/p>
&lt;p>So why waste time trying to solve problems you haven’t even encountered yet? Focus on the problems you &lt;em>do&lt;/em> have—like finding product-market fit and getting your first few users through the door.&lt;/p>
&lt;h3 id="scaling-when-you-need-to-is-easier-than-you-think">&lt;strong>Scaling When You Need To Is Easier Than You Think&lt;/strong>&lt;/h3>
&lt;p>The good news is that scaling isn’t as hard as it used to be. Cloud platforms like AWS, Google Cloud, and Microsoft Azure make it easier than ever to add resources when you need them. You don’t need to be ready for millions of users from the start because modern infrastructure can scale up when the time comes.&lt;/p>
&lt;p>When you hit that growth spurt, you’ll have tools available to scale quickly. Until then, keep your setup simple and cost-effective.&lt;/p>
&lt;h3 id="user-experience-matters-more-than-scale">&lt;strong>User Experience Matters More Than Scale&lt;/strong>&lt;/h3>
&lt;p>No one’s going to care how scalable your app is if it’s not enjoyable to use. If your early users are running into bugs, confusing interfaces, or missing features, it won’t matter how many servers you’ve got ready to handle millions more.&lt;/p>
&lt;p>Your top priority right now should be creating a product that people love. The smoother and more enjoyable their experience, the more likely they are to stick around—and tell others about it. Get the basics right first, and worry about scale later.&lt;/p>
&lt;h3 id="in-summary-build-for-now-not-for-someday">In Summary: Build for Now, Not for &amp;ldquo;Someday&amp;rdquo;&lt;/h3>
&lt;p>When you&amp;rsquo;re just starting out, it’s tempting to prepare for a massive influx of users. But designing for scale when you’re still trying to get your first 100 users is overkill. It wastes time, money, and energy that could be better spent on improving your product and growing your user base.&lt;/p>
&lt;p>Right now, your focus should be on creating something people want, shipping quickly, and learning from your users. If millions of users come down the line, you&amp;rsquo;ll be ready for them—but you don’t need to be ready today.&lt;/p>
&lt;p>Build for the present, and let the future take care of itself when it arrives.&lt;/p></description></item><item><title>Why Copilot is Making Programmers Worse at Programming</title><link>https://www.darrenhorrocks.co.uk/why-copilot-making-programmers-worse-at-programming/</link><pubDate>Wed, 11 Sep 2024 11:47:58 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-copilot-making-programmers-worse-at-programming/</guid><description>&lt;p>Over the past few years, the evolution of AI-driven tools like GitHub&amp;rsquo;s Copilot and other large language models (LLMs) has promised to revolutionise programming. By leveraging deep learning, these tools can generate code, suggest solutions, and even troubleshoot issues in real-time, saving developers hours of work. While these tools have obvious benefits in terms of productivity, there&amp;rsquo;s a growing concern that they may also have unintended consequences on the quality and skillset of programmers.&lt;/p>
&lt;h3 id="erosion-of-core-programming-skills">&lt;strong>Erosion of Core Programming Skills&lt;/strong>&lt;/h3>
&lt;p>One of the most significant risks of relying on tools like Copilot is the gradual erosion of fundamental programming skills. In the past, learning to code involved hands-on problem-solving, debugging, and a deep understanding of how code works at various levels—from algorithms to low-level implementation details.&lt;/p>
&lt;p>AI assistants, while useful, often allow developers to bypass these steps. For instance, rather than deeply understanding the underlying structure of algorithms or learning how to write efficient loops and recursion, programmers can now just accept auto-generated code snippets. Over time, this could lead to developers who can&amp;rsquo;t effectively solve problems without an AI&amp;rsquo;s assistance because they never fully developed the problem-solving and critical-thinking skills necessary for programming.&lt;/p>
&lt;h3 id="over-reliance-on-auto-generated-code">&lt;strong>Over-Reliance on Auto-Generated Code&lt;/strong>&lt;/h3>
&lt;p>With tools like Copilot, developers can quickly produce working code without fully understanding its mechanics. This leads to what&amp;rsquo;s known as “code dependency”, where developers lean too heavily on AI-generated solutions without checking for correctness, efficiency, or maintainability.&lt;/p>
&lt;p>One key issue with auto-generated code is that it may not always be the optimal solution for a specific problem. Without proper vetting, programmers might accept inefficient, buggy, or insecure code that works at first glance but causes problems in the long run. This reliance reduces the incentive to refactor or even review the code, which could harm the codebase and team productivity over time.&lt;/p>
&lt;h3 id="lack-of-ownership-and-responsibility">&lt;strong>Lack of Ownership and Responsibility&lt;/strong>&lt;/h3>
&lt;p>AI-assisted code generation can lead to a phenomenon where developers become detached from the code they &amp;ldquo;write&amp;rdquo;. When a developer writes every line of code manually, they take full responsibility for its behaviour, whether it&amp;rsquo;s functional, secure, or efficient. In contrast, when AI generates significant portions of code, it&amp;rsquo;s easy to shift that sense of responsibility onto the AI assistant.&lt;/p>
&lt;p>This lack of ownership can make developers complacent, thinking, “The AI generated it, so it must be correct”. But AI-generated code isn&amp;rsquo;t immune to errors, bugs, or even security vulnerabilities. Ultimately, it&amp;rsquo;s the developer&amp;rsquo;s job to review, understand, and refine the code, but with the convenience of Copilot, that diligence might fade.&lt;/p>
&lt;h3 id="reduced-learning-opportunities">&lt;strong>Reduced Learning Opportunities&lt;/strong>&lt;/h3>
&lt;p>One of the most crucial aspects of becoming a great programmer is the continuous learning process. Every bug encountered, every design decision made, and every algorithm researched is an opportunity to learn something new. However, by providing quick solutions, Copilot and other LLMs may shortcut the learning process, giving developers answers without requiring them to dig deeper into why certain solutions work or don&amp;rsquo;t work.&lt;/p>
&lt;p>When a tool hands you the solution immediately, you&amp;rsquo;re less likely to seek alternative approaches, experiment with different methods, or fully understand the trade-offs of different implementations. This convenience, while beneficial in the short term, reduces the number of learning experiences that contribute to long-term skill development.&lt;/p>
&lt;h3 id="narrowed-creative-thinking">&lt;strong>Narrowed Creative Thinking&lt;/strong>&lt;/h3>
&lt;p>Programming is as much about creativity as it is about logic. A skilled programmer can approach a problem from multiple angles and come up with a variety of solutions, weighing the pros and cons of each. By offering suggestions that are often based on existing code or patterns, AI-driven tools can limit a developer&amp;rsquo;s exploration of novel approaches or innovative solutions.&lt;/p>
&lt;p>While Copilot can suggest “what works”, it may also promote more conventional or popular patterns at the expense of encouraging out-of-the-box thinking. The fear is that AI tools might cause programming to become a more mechanical process of accepting suggestions rather than a creative pursuit that pushes boundaries.&lt;/p>
&lt;h3 id="dependency-on-proprietary-tools">&lt;strong>Dependency on Proprietary Tools&lt;/strong>&lt;/h3>
&lt;p>Another downside of AI-driven tools is the dependency they create on proprietary platforms like GitHub Copilot. When developers start using these tools, they become increasingly reliant on them to generate, troubleshoot, or optimise their code. This creates a problem when the tools fail, change their terms of service, or become too expensive.&lt;/p>
&lt;p>Additionally, this dependency on AI tools can isolate developers from broader programming communities and open-source tools that encourage peer collaboration and knowledge sharing, further hindering the growth of their skills.&lt;/p>
&lt;h3 id="false-sense-of-expertise">&lt;strong>False Sense of Expertise&lt;/strong>&lt;/h3>
&lt;p>Perhaps one of the most concerning effects of AI code generation is that it can create a false sense of expertise among developers. A developer might feel proficient in programming because they can quickly generate working code with the help of Copilot, even if they don&amp;rsquo;t fully understand the code.&lt;/p>
&lt;p>This can be particularly dangerous when developers move into more complex areas of software engineering, like performance optimization, concurrency, or security, where a deeper understanding of the code is critical. Without the foundational knowledge, developers can make mistakes that are costly in both time and money.&lt;/p>
&lt;h3 id="in-short">In Short&lt;/h3>
&lt;p>If you as the developer do not understand the code, do not understand how you got to where you are, do not understand how to solve the problem yourself. Copying and pasting code from a LLM, and being spoon fed the answer, is not going to make you a better programmer. Its going to make you reliant on the robot, and you will never be able to do anything that the robot cant already do.&lt;/p></description></item><item><title>Five Reasons Visual Studio is Better than Rider</title><link>https://www.darrenhorrocks.co.uk/five-reasons-visual-studio-better-than-rider/</link><pubDate>Wed, 11 Sep 2024 10:57:38 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/five-reasons-visual-studio-better-than-rider/</guid><description>&lt;p>JetBrains Rider has garnered attention as a modern, cross-platform IDE, especially for .NET developers. However, Visual Studio remains the reigning heavyweight in the development world, thanks to years of evolution and refinement. While Rider offers impressive features, there are still several areas where Visual Studio shines brighter. Here are five reasons why Visual Studio is better than Rider.&lt;/p>
&lt;h3 id="comprehensive-microsoft-ecosystem-integration">&lt;strong>Comprehensive Microsoft Ecosystem Integration&lt;/strong>&lt;/h3>
&lt;p>Visual Studio is deeply integrated into the &lt;strong>Microsoft ecosystem&lt;/strong>, making it the default choice for developers working with Microsoft technologies like .NET, Azure, and SQL Server. It offers out-of-the-box features and first-class support for tools such as &lt;strong>Azure DevOps&lt;/strong>, &lt;strong>Azure Functions&lt;/strong>, and &lt;strong>Entity Framework&lt;/strong>, with easy configuration and deployment options.&lt;/p>
&lt;p>While Rider offers solid support for the .NET ecosystem, Visual Studio’s native integration with Microsoft&amp;rsquo;s cloud and enterprise solutions makes it a one-stop shop for developers working in the Microsoft stack. Whether you need to deploy to Azure, use Microsoft-specific libraries, or configure cloud services, Visual Studio’s deep integration gives it a significant edge.&lt;/p>
&lt;h3 id="powerful-debugging-tools">&lt;strong>Powerful Debugging Tools&lt;/strong>&lt;/h3>
&lt;p>Visual Studio has some of the &lt;strong>best debugging tools&lt;/strong> in the industry. It provides advanced features like &lt;strong>Edit and Continue&lt;/strong>, &lt;strong>Live Unit Testing&lt;/strong>, and &lt;strong>IntelliTrace&lt;/strong> (for historical debugging), which help developers diagnose and fix issues more efficiently.&lt;/p>
&lt;p>While Rider has strong debugging capabilities, it doesn&amp;rsquo;t quite match Visual Studio’s level of depth, especially when it comes to enterprise-level applications. &lt;strong>IntelliTrace&lt;/strong>, for instance, allows developers to step back through time during debugging sessions, making it easier to pinpoint and solve complex issues. This feature is particularly valuable for debugging large applications with many moving parts, where standard breakpoints and stepping through code aren’t enough.&lt;/p>
&lt;h3 id="comprehensive-support-for-gui-based-development">&lt;strong>Comprehensive Support for GUI-Based Development&lt;/strong>&lt;/h3>
&lt;p>For developers working on &lt;strong>Windows desktop applications&lt;/strong>, especially using technologies like &lt;strong>WPF&lt;/strong>, &lt;strong>Windows Forms&lt;/strong>, or &lt;strong>UWP&lt;/strong>, Visual Studio provides superior support with its &lt;strong>drag-and-drop designers&lt;/strong> and rich tooling for building GUI applications. The visual designers for XAML and WinForms allow for rapid prototyping, with a real-time preview of the UI as it’s being built.&lt;/p>
&lt;p>While Rider offers basic support for WPF and other desktop frameworks, its GUI development tools are not as mature or fully featured as Visual Studio’s. The &lt;strong>designer experience&lt;/strong> in Visual Studio is a major advantage for developers building desktop apps that require a lot of UI work.&lt;/p>
&lt;h3 id="more-mature-ecosystem-and-extensions">&lt;strong>More Mature Ecosystem and Extensions&lt;/strong>&lt;/h3>
&lt;p>Visual Studio has a massive and &lt;strong>mature ecosystem of extensions&lt;/strong> available through the Visual Studio Marketplace. Whether you need tools for version control, database management, or third-party integrations, you’ll find a wide variety of well-supported plugins. Popular tools like &lt;strong>CodeRush&lt;/strong>, &lt;strong>Visual Assist&lt;/strong>, and &lt;strong>SpecFlow&lt;/strong> are readily available and often offer better integration with Visual Studio than with Rider.&lt;/p>
&lt;p>Rider has its own plugin marketplace, but it’s smaller in scale and doesn&amp;rsquo;t yet offer the same variety or depth of support that Visual Studio does. For developers who rely heavily on custom tooling and extensions to enhance their workflow, Visual Studio remains the more versatile platform.&lt;/p>
&lt;h3 id="enterprise-features-and-licensing">&lt;strong>Enterprise Features and Licensing&lt;/strong>&lt;/h3>
&lt;p>Visual Studio provides &lt;strong>enterprise-grade features&lt;/strong>, particularly in the &lt;strong>Visual Studio Enterprise edition&lt;/strong>. Features like &lt;strong>Live Dependency Validation&lt;/strong>, &lt;strong>Code Map&lt;/strong>, and &lt;strong>Architectural Layer Diagrams&lt;/strong> are invaluable in large-scale enterprise environments. These tools allow teams to visualize complex project dependencies, validate architectural decisions, and ensure code quality at scale.&lt;/p>
&lt;p>Rider’s offering is more focused on small to medium-sized teams, and while it includes many high-quality features for individual developers, it lacks some of the comprehensive enterprise tools available in Visual Studio. Additionally, Visual Studio’s enterprise licensing model provides extensive support and services for large organizations, making it an ideal choice for mission-critical applications in business environments.&lt;/p>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>While Rider is an excellent alternative IDE, Visual Studio remains the leader in several key areas. Its deep integration with the &lt;strong>Microsoft ecosystem&lt;/strong>, &lt;strong>advanced debugging tools&lt;/strong>, superior &lt;strong>GUI development features&lt;/strong>, &lt;strong>robust extension marketplace&lt;/strong>, and enterprise-focused features make it a powerhouse for developers working on .NET, cloud services, or large-scale enterprise applications. For many developers, especially those heavily invested in Microsoft technologies, Visual Studio is still the IDE of choice.&lt;/p></description></item><item><title>Five Reasons Rider is Better than Visual Studio</title><link>https://www.darrenhorrocks.co.uk/five-reasons-rider-better-than-visual-studio/</link><pubDate>Wed, 11 Sep 2024 10:57:17 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/five-reasons-rider-better-than-visual-studio/</guid><description>&lt;p>When it comes to .NET development, Visual Studio has long been the go-to integrated development environment (IDE). However, JetBrains' Rider has emerged as a serious competitor, offering a fresh alternative with features that many developers now prefer. While both are powerful tools, here are five reasons why Rider is considered better than Visual Studio for many developers.&lt;/p>
&lt;h3 id="cross-platform-support">&lt;strong>Cross-Platform Support&lt;/strong>&lt;/h3>
&lt;p>One of Rider’s most significant advantages is its &lt;strong>cross-platform compatibility&lt;/strong>. Rider runs smoothly on Windows, macOS, and Linux, providing a consistent development experience across different operating systems. This is a game-changer for developers who prefer using macOS or Linux for development but need access to .NET technologies, which traditionally were more Windows-centric.&lt;/p>
&lt;p>Visual Studio, on the other hand, is largely limited to Windows. While Visual Studio for Mac exists, it lags behind in features and performance compared to its Windows counterpart, making Rider the superior option for cross-platform .NET development.&lt;/p>
&lt;h3 id="faster-performance">&lt;strong>Faster Performance&lt;/strong>&lt;/h3>
&lt;p>Rider is widely praised for its &lt;strong>speed and responsiveness&lt;/strong>. Built on the IntelliJ platform (the same engine powering JetBrains’ other IDEs like IntelliJ IDEA), Rider is optimized to handle large projects with ease. It doesn’t suffer from the sluggishness or memory bloat often associated with Visual Studio, particularly in resource-heavy projects.&lt;/p>
&lt;p>The performance gap is even more noticeable when working with solutions that contain multiple projects. Rider’s ability to index files efficiently and provide fast code navigation and refactoring tools allows developers to work without interruptions.&lt;/p>
&lt;h3 id="advanced-code-analysis-and-refactoring">&lt;strong>Advanced Code Analysis and Refactoring&lt;/strong>&lt;/h3>
&lt;p>JetBrains is known for its superior code analysis tools, and Rider continues this tradition. It provides &lt;strong>real-time code inspections&lt;/strong>, detecting issues as you type, and offering intelligent suggestions for code improvements. Rider integrates tools from &lt;strong>ReSharper&lt;/strong> (JetBrains’ popular Visual Studio extension), giving you access to an extensive set of refactoring and code transformation tools right out of the box.&lt;/p>
&lt;p>In Visual Studio, you typically need to install ReSharper as an add-on, which can significantly slow down the IDE. With Rider, ReSharper&amp;rsquo;s capabilities are built-in and optimized, providing advanced refactorings, code fixes, and insights without performance penalties.&lt;/p>
&lt;h3 id="unified-experience-for-multiple-languages">&lt;strong>Unified Experience for Multiple Languages&lt;/strong>&lt;/h3>
&lt;p>Rider is more than just a .NET IDE; it’s also a robust tool for &lt;strong>polyglot developers&lt;/strong>. It supports a wide variety of languages beyond C#, such as JavaScript, TypeScript, HTML, CSS, and more. This makes it an excellent choice for full-stack developers working across the .NET ecosystem and front-end technologies.&lt;/p>
&lt;p>In contrast, Visual Studio often requires separate extensions for working with different languages, and its support can be inconsistent or not as smooth as in Rider. Rider’s seamless integration of multiple language support under one roof makes for a streamlined, unified development experience.&lt;/p>
&lt;h3 id="comprehensive-git-integration">&lt;strong>Comprehensive Git Integration&lt;/strong>&lt;/h3>
&lt;p>Rider excels in its &lt;strong>version control integration&lt;/strong>, particularly with Git. Its user interface for Git actions is intuitive, allowing you to stage, commit, rebase, and merge changes directly within the IDE with ease. Rider’s Git tool window provides a clear visual representation of commits, branches, and diffs, which helps developers better understand their code history and manage branches effectively.&lt;/p>
&lt;p>Visual Studio’s Git support has improved over time, but it still lacks the depth and usability of Rider’s implementation. Rider’s superior merge conflict resolution, interactive rebase tools, and comprehensive commit history make it a better choice for developers who heavily rely on Git.&lt;/p>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>Both Rider and Visual Studio are excellent IDEs, but Rider’s &lt;strong>cross-platform support&lt;/strong>, &lt;strong>performance&lt;/strong>, &lt;strong>advanced code analysis&lt;/strong>, &lt;strong>multi-language capabilities&lt;/strong>, and &lt;strong>superior Git integration&lt;/strong> make it a compelling alternative to Visual Studio, especially for developers working on complex .NET projects or those who want a more versatile, streamlined development environment. Whether you’re a long-time .NET developer or exploring new tools, Rider is definitely worth a look.&lt;/p></description></item><item><title>The Single Best and Worst Things About Popular Programming Languages</title><link>https://www.darrenhorrocks.co.uk/single-best-worst-things-about-popular-programming-languages/</link><pubDate>Wed, 11 Sep 2024 10:37:49 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/single-best-worst-things-about-popular-programming-languages/</guid><description>&lt;p>I use a lot of programming languages, I use them in different ways for different things because they are tools to do a job, just like a hammer is a tool to hammer in nails and a screwdriver (shockingly) drives screws&amp;hellip; And while you can hammer in nails with a screwdriver, and you can drive screws with a hammer&amp;hellip; They are not the best tools for those jobs.&lt;/p>
&lt;p>So lets have a look at what jobs that some given languages, are good at and what they are bad at&amp;hellip;&lt;/p>
&lt;h3 id="c">&lt;strong>C&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Low-Level Control&lt;/strong>
&lt;ul>
&lt;li>C provides an unparalleled level of control over system resources like memory and hardware. This is why it’s the foundation of operating systems, embedded systems, and high-performance applications.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Manual Memory Management&lt;/strong>
&lt;ul>
&lt;li>The flip side of C’s low-level control is that it requires the programmer to manage memory manually. This can lead to errors like memory leaks and segmentation faults, which can be tricky to debug.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="c-1">&lt;strong>C++&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Object-Oriented with High Performance&lt;/strong>
&lt;ul>
&lt;li>C++ combines the power of C with object-oriented features, making it suitable for high-performance applications that still need abstractions like classes and inheritance.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Complexity&lt;/strong>
&lt;ul>
&lt;li>C++ is notoriously complex, with a vast array of features, from multiple inheritance to template metaprogramming, which can be difficult to learn and prone to misuse.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="c-2">&lt;strong>C#&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Integrated Development with .NET&lt;/strong>
&lt;ul>
&lt;li>C# is tightly integrated with Microsoft’s .NET ecosystem, making it easy to develop cross-platform applications, particularly desktop and web apps, with powerful development tools like Visual Studio.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Platform Dependency (Historically)&lt;/strong>
&lt;ul>
&lt;li>While cross-platform support has improved with .NET Core and later versions, C# was long seen as a Microsoft-centric language, limiting its appeal to developers working outside the Windows ecosystem.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="java">&lt;strong>Java&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Write Once, Run Anywhere&lt;/strong>
&lt;ul>
&lt;li>Java’s promise of platform independence through the Java Virtual Machine (JVM) allows code to run on any system that has a JVM, making it a top choice for enterprise applications.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Verbose Syntax&lt;/strong>
&lt;ul>
&lt;li>Java’s strict object-oriented nature and requirement for boilerplate code, like getters and setters, make it more verbose than other languages, leading to more typing and less concise code.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="javascript">&lt;strong>JavaScript&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Ubiquity in Web Development&lt;/strong>
&lt;ul>
&lt;li>JavaScript is the only programming language natively supported by all web browsers, making it indispensable for front-end web development. Its versatility with frameworks like Node.js also extends its reach to the server side.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Inconsistent Behavior&lt;/strong>
&lt;ul>
&lt;li>JavaScript&amp;rsquo;s quirks, such as type coercion and confusing scoping rules, often result in unexpected behavior, leading to frustration, especially for beginners.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="rust">&lt;strong>Rust&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Memory Safety Without a Garbage Collector&lt;/strong>
&lt;ul>
&lt;li>Rust’s unique ownership model ensures memory safety and prevents common bugs like null pointer dereferencing or data races, all without needing a garbage collector.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Steep Learning Curve&lt;/strong>
&lt;ul>
&lt;li>Rust’s novel concepts, particularly around ownership and lifetimes, can be challenging to grasp, even for experienced developers coming from other languages.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="python">&lt;strong>Python&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Readability and Simplicity&lt;/strong>
&lt;ul>
&lt;li>Python’s clean and human-readable syntax makes it an ideal language for beginners, while its vast libraries and frameworks make it powerful enough for professional use in data science, web development, and automation.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Performance&lt;/strong>
&lt;ul>
&lt;li>Python is an interpreted language and tends to be slower than compiled languages like C or Go, making it less ideal for performance-critical applications.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="php">&lt;strong>PHP&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Web Development Simplicity&lt;/strong>
&lt;ul>
&lt;li>PHP is designed for the web, and its tight integration with HTML makes it extremely simple to create dynamic web pages. This makes it a popular choice for content management systems like WordPress.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Inconsistent Design&lt;/strong>
&lt;ul>
&lt;li>PHP has evolved organically, leading to inconsistent naming conventions, inconsistent function arguments, and a general feeling of &amp;ldquo;messiness&amp;rdquo; in larger projects.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="ruby">&lt;strong>Ruby&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Elegant Syntax&lt;/strong>
&lt;ul>
&lt;li>Ruby’s principle of &amp;ldquo;optimizing for programmer happiness&amp;rdquo; is reflected in its elegant and expressive syntax, making it a joy to read and write, especially when using frameworks like Ruby on Rails.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Performance Issues&lt;/strong>
&lt;ul>
&lt;li>Ruby can be slow in comparison to languages like Python or Java, especially when it comes to CPU-bound operations, which may limit its use in high-performance applications.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="swift">&lt;strong>Swift&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Modern Language for Apple Ecosystem&lt;/strong>
&lt;ul>
&lt;li>Swift was designed to be fast, safe, and expressive, and it has become the go-to language for developing iOS and macOS apps. Its syntax is concise yet powerful, which improves developer productivity.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Apple-Only Focus&lt;/strong>
&lt;ul>
&lt;li>Despite its technical merits, Swift’s primary use case is still within the Apple ecosystem, limiting its reach for developers who aren’t working on Apple platforms.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="go">&lt;strong>Go&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Concurrency Support&lt;/strong>
&lt;ul>
&lt;li>Go was designed with concurrency in mind, and its goroutines provide a simple yet powerful way to handle multiple tasks simultaneously, making it ideal for distributed systems and network services.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Limited Language Features&lt;/strong>
&lt;ul>
&lt;li>While Go’s simplicity is a strength, some developers find it too minimalistic, lacking features like generics (which were added only in later versions) and comprehensive error handling patterns.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="kotlin">&lt;strong>Kotlin&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Interoperability with Java&lt;/strong>
&lt;ul>
&lt;li>Kotlin’s seamless interoperability with Java, combined with modern language features like null safety and extension functions, make it an ideal choice for Android development and projects with existing Java codebases.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Limited Ecosystem Outside the JVM&lt;/strong>
&lt;ul>
&lt;li>Although Kotlin has been growing in popularity, especially for Android development, its ecosystem outside of JVM-related applications is still limited compared to more established languages.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="assembly">&lt;strong>Assembly&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Maximum Control Over Hardware&lt;/strong>
&lt;ul>
&lt;li>Writing in Assembly allows developers to control the hardware at the instruction level, making it essential for developing highly optimized, low-level software like drivers and bootloaders.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Complexity and Readability&lt;/strong>
&lt;ul>
&lt;li>Assembly is difficult to write and even harder to read. It requires a deep understanding of the underlying hardware and is impractical for most modern software development tasks.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="objective-c">&lt;strong>Objective-C&lt;/strong>&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Best Thing:&lt;/strong> &lt;strong>Mature Language for macOS and iOS&lt;/strong>
&lt;ul>
&lt;li>Objective-C has been the backbone of Apple development for decades, and it has a rich ecosystem of libraries, frameworks, and tools designed for macOS and iOS applications.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Worst Thing:&lt;/strong> &lt;strong>Verbose Syntax&lt;/strong>
&lt;ul>
&lt;li>Objective-C’s syntax is notoriously verbose, particularly when compared to newer languages like Swift. This can make code harder to read and write, slowing down development.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>This is by no means an exhaustive list, nor is it a concrete guide of &amp;ldquo;this is how you should do things&amp;rdquo;, this is just what I have found over the years and what I personally have found to be the best and worst parts of these languages and what they are good at, and not good at. What languages do you use, and what do you think they are or are not good at?&lt;/p></description></item><item><title>HTTP Server Tutorial in Rust</title><link>https://www.darrenhorrocks.co.uk/rust-http-server-tutorial/</link><pubDate>Tue, 23 Jul 2024 22:11:39 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/rust-http-server-tutorial/</guid><description>&lt;p>In the modern web landscape, understanding how HTTP servers work under the hood can be incredibly empowering. While there are many frameworks and libraries available that abstract away the complexities of building an HTTP server, sometimes it&amp;rsquo;s valuable to get back to basics and see how things work from the ground up.&lt;/p>
&lt;p>In this tutorial, we will guide you through building a simple HTTP server in Rust using only the standard library, without relying on any external crates. This will give you a solid understanding of network programming in Rust and a foundation you can build on to create more complex servers in the future.&lt;/p>
&lt;p>In Rust, the &lt;code>main&lt;/code> function is the entry point of any executable program. Here&amp;rsquo;s the complete &lt;code>main&lt;/code> function from our server code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;span style="color:#66d9ef">let&lt;/span> listener &lt;span style="color:#f92672">=&lt;/span> TcpListener::bind(&lt;span style="color:#e6db74">&amp;#34;127.0.0.1:3000&amp;#34;&lt;/span>).unwrap();
println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Listening on port 3000...&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">for&lt;/span> stream &lt;span style="color:#66d9ef">in&lt;/span> listener.incoming() {
&lt;span style="color:#66d9ef">match&lt;/span> stream {
Ok(stream) &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
thread::spawn(&lt;span style="color:#66d9ef">move&lt;/span> &lt;span style="color:#f92672">||&lt;/span> {
handle_client(stream);
});
}
Err(e) &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
eprintln&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Error: {}&amp;#34;&lt;/span>, e);
}
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-by-step-breakdown">Step-by-Step Breakdown&lt;/h3>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Binding to an Address&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">let&lt;/span> listener &lt;span style="color:#f92672">=&lt;/span> TcpListener::bind(&lt;span style="color:#e6db74">&amp;#34;127.0.0.1:3000&amp;#34;&lt;/span>).unwrap();
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>TcpListener::bind&lt;/strong>: This function binds the server to the specified IP address and port. Here, &lt;code>127.0.0.1:3000&lt;/code> means the server will listen on localhost, port 3000.&lt;/li>
&lt;li>&lt;strong>unwrap()&lt;/strong>: This function is used for error handling. It will panic if binding fails. In a production server, you might want to handle this error more gracefully.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Logging Server Status&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">println&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Listening on port 3000...&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>This line prints a message to the console, indicating that the server is up and running and listening on port 3000.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Accepting Incoming Connections&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">for&lt;/span> stream &lt;span style="color:#66d9ef">in&lt;/span> listener.incoming() {
&lt;span style="color:#66d9ef">match&lt;/span> stream {
Ok(stream) &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
thread::spawn(&lt;span style="color:#66d9ef">move&lt;/span> &lt;span style="color:#f92672">||&lt;/span> {
handle_client(stream);
});
}
Err(e) &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
eprintln&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Error: {}&amp;#34;&lt;/span>, e);
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>
&lt;p>&lt;strong>listener.incoming()&lt;/strong>: This method returns an iterator over incoming connections. It will block until a client connects.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>for stream in listener.incoming()&lt;/strong>: This loop iterates over each incoming connection. For each connection, &lt;code>stream&lt;/code> is a &lt;code>TcpStream&lt;/code> instance.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Handling Each Connection&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">match&lt;/span> stream {
Ok(stream) &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
thread::spawn(&lt;span style="color:#66d9ef">move&lt;/span> &lt;span style="color:#f92672">||&lt;/span> {
handle_client(stream);
});
}
Err(e) &lt;span style="color:#f92672">=&amp;gt;&lt;/span> {
eprintln&lt;span style="color:#f92672">!&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Error: {}&amp;#34;&lt;/span>, e);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>match stream&lt;/strong>: This pattern matches the result of &lt;code>listener.incoming()&lt;/code>. It handles two cases:
&lt;ul>
&lt;li>&lt;strong>Ok(stream)&lt;/strong>: If a connection is successfully established, &lt;code>stream&lt;/code> is a &lt;code>TcpStream&lt;/code> object representing the connection.
&lt;ul>
&lt;li>&lt;strong>thread::spawn(move || { handle_client(stream); })&lt;/strong>: This spawns a new thread to handle the client connection. The &lt;code>move&lt;/code> keyword moves &lt;code>stream&lt;/code> into the closure, allowing it to be accessed within the thread.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Err(e)&lt;/strong>: If an error occurs while accepting a connection, it prints the error message to &lt;code>stderr&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="example-of-handling-a-client-connection">Example of Handling a Client Connection&lt;/h3>
&lt;p>Inside the &lt;code>thread::spawn&lt;/code> closure, we call &lt;code>handle_client(stream)&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">thread::spawn(&lt;span style="color:#66d9ef">move&lt;/span> &lt;span style="color:#f92672">||&lt;/span> {
handle_client(stream);
});
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This line starts a new thread and calls the &lt;code>handle_client&lt;/code> function, passing the &lt;code>stream&lt;/code> as an argument. Here’s what &lt;code>handle_client&lt;/code> looks like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-rust" data-lang="rust">&lt;span style="color:#66d9ef">fn&lt;/span> &lt;span style="color:#a6e22e">handle_client&lt;/span>(&lt;span style="color:#66d9ef">mut&lt;/span> stream: &lt;span style="color:#a6e22e">TcpStream&lt;/span>) {
&lt;span style="color:#66d9ef">let&lt;/span> &lt;span style="color:#66d9ef">mut&lt;/span> buffer &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#ae81ff">1024&lt;/span>];
&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#66d9ef">let&lt;/span> Ok(_) &lt;span style="color:#f92672">=&lt;/span> stream.read(&lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#66d9ef">mut&lt;/span> buffer) {
&lt;span style="color:#66d9ef">let&lt;/span> response &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">let&lt;/span> _ &lt;span style="color:#f92672">=&lt;/span> stream.write(response.as_bytes());
&lt;span style="color:#66d9ef">let&lt;/span> _ &lt;span style="color:#f92672">=&lt;/span> stream.flush();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>Reading from the Stream&lt;/strong>: &lt;code>stream.read(&amp;amp;mut buffer)&lt;/code> reads the incoming request into the buffer.&lt;/li>
&lt;li>&lt;strong>Constructing the Response&lt;/strong>: We create a simple HTTP response as a string.&lt;/li>
&lt;li>&lt;strong>Writing the Response&lt;/strong>: &lt;code>stream.write(response.as_bytes())&lt;/code> sends the response back to the client, and &lt;code>stream.flush()&lt;/code> ensures all data is sent.&lt;/li>
&lt;/ul>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>The &lt;code>main&lt;/code> function is the heart of our server, managing incoming connections and delegating client handling to separate threads. This modular approach ensures that the server remains responsive and can handle multiple clients simultaneously.&lt;/p>
&lt;p>Feel free to ask if you have any specific questions or need further clarifications on any part of the code!&lt;/p></description></item><item><title>Why GraphQL is Better Than Basic REST and SOAP APIs</title><link>https://www.darrenhorrocks.co.uk/why-graphql-better-than-basic-rest-soap-apis/</link><pubDate>Tue, 04 Jun 2024 14:35:03 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-graphql-better-than-basic-rest-soap-apis/</guid><description>&lt;p>SOAP and REST have been the dominant protocols for years. However, GraphQL has emerged in the last few years as a powerful alternative, offering numerous advantages over its
predecessors. In this article, we&amp;rsquo;ll explore why GraphQL is often considered better than basic REST and SOAP APIs, and why you might want to consider it for your next project.&lt;/p>
&lt;h3 id="understanding-the-basics">Understanding the Basics&lt;/h3>
&lt;h4 id="soap">SOAP&lt;/h4>
&lt;p>SOAP (Simple Object Access Protocol) is a protocol for exchanging structured information in the implementation of web services. It relies on XML-based messaging and is known for its strong standards and security features. SOAP is often used in enterprise-level applications where reliability and security are paramount.&lt;/p>
&lt;h4 id="rest">REST&lt;/h4>
&lt;p>REST (Representational State Transfer) is an architectural style that uses standard HTTP methods and is known for its simplicity and ease of use. REST APIs typically exchange data in JSON format and are widely used for web and mobile applications due to their flexibility and ease of integration.&lt;/p>
&lt;h4 id="graphql">GraphQL&lt;/h4>
&lt;p>GraphQL, developed by Facebook in 2012 and released publicly in 2015, is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. It enables clients to request exactly the data they need, nothing more, and nothing less.&lt;/p>
&lt;h3 id="why-graphql-is-better-than-rest-and-soap">Why GraphQL is Better Than REST and SOAP&lt;/h3>
&lt;h4 id="1-precise-data-fetching">1. Precise Data Fetching&lt;/h4>
&lt;p>One of the most significant advantages of GraphQL is its ability to allow clients to request exactly the data they need. This contrasts sharply with REST, where you might need multiple endpoints to gather related data, often leading to over-fetching or under-fetching of data.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>
In REST, to get user information along with their posts, you might have to hit two endpoints:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">GET /users/1
GET /users/1/posts
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In GraphQL, a single query can fetch exactly what&amp;rsquo;s needed:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">{
user(id: &lt;span style="color:#a6e22e">1&lt;/span>) {
name
posts {
title
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2-strongly-typed-schema">2. Strongly Typed Schema&lt;/h4>
&lt;p>GraphQL APIs are defined by a schema, which is a strong, self-documenting contract between the server and the client. This schema is introspectable, allowing developers to understand the structure of the API without external documentation. REST and SOAP do not enforce a single way to define and discover the API&amp;rsquo;s schema, leading to inconsistencies and increased reliance on external documentation.&lt;/p>
&lt;h4 id="3-single-endpoint">3. Single Endpoint&lt;/h4>
&lt;p>GraphQL operates on a single endpoint, which simplifies the API structure and reduces complexity in routing. In contrast, REST typically involves multiple endpoints for different resources, which can become cumbersome to manage as an application grows.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>
A REST API might have the following endpoints:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">GET /users
GET /posts
GET /comments
&lt;/code>&lt;/pre>&lt;/div>&lt;p>A GraphQL API would have a single endpoint, e.g., &lt;code>/graphql&lt;/code>, where all queries and mutations are directed.&lt;/p>
&lt;h4 id="4-reduced-overhead">4. Reduced Overhead&lt;/h4>
&lt;p>GraphQL reduces the number of HTTP requests needed to fetch related data. This is particularly beneficial for applications with complex relationships and nested data structures. By reducing the number of round-trips between the client and server, GraphQL can lead to improved performance and a better user experience.&lt;/p>
&lt;h4 id="5-better-developer-experience">5. Better Developer Experience&lt;/h4>
&lt;p>GraphQL&amp;rsquo;s introspection capabilities allow for powerful development tools, such as GraphiQL, an in-browser IDE for exploring GraphQL APIs. These tools can significantly enhance the developer experience by providing real-time feedback and auto-completion.&lt;/p>
&lt;h4 id="6-evolution-without-versioning">6. Evolution Without Versioning&lt;/h4>
&lt;p>GraphQL allows APIs to evolve without the need for versioning. New fields and types can be added to the GraphQL schema without impacting existing queries. In contrast, REST APIs often rely on versioning to manage changes, which can lead to fragmentation and increased maintenance overhead.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>
Adding a new field in REST might involve creating a new versioned endpoint:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-plain" data-lang="plain">GET /v2/users/1
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In GraphQL, you simply add the new field to the schema, and clients can start querying it immediately.&lt;/p>
&lt;h3 id="use-cases-where-graphql-excels">Use Cases Where GraphQL Excels&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>Mobile and Single-Page Applications:&lt;/strong> These applications often require efficient data fetching to improve performance and user experience.&lt;/li>
&lt;li>&lt;strong>Complex Systems and Microservices:&lt;/strong> GraphQL can serve as an aggregation layer, fetching data from multiple sources and presenting it through a unified API.&lt;/li>
&lt;li>&lt;strong>APIs with Rapidly Changing Requirements:&lt;/strong> The flexibility of GraphQL allows for quick adaptation without the need for extensive versioning and maintenance.&lt;/li>
&lt;/ol>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>While REST and SOAP have their strengths and are still widely used, GraphQL offers a modern approach that addresses many of the pain points associated with these older technologies. Its ability to fetch precise data, introspectable schema, single endpoint architecture, reduced overhead, enhanced developer experience, and smooth evolution without versioning make it a compelling choice for many applications. If you&amp;rsquo;re looking to build an efficient, scalable, and developer-friendly API, GraphQL is certainly worth considering.&lt;/p></description></item><item><title>Save KSP2 - An Open Letter to Take-Two and CEO Strauss Zelnick</title><link>https://www.darrenhorrocks.co.uk/save-ksp2-an-open-letter-to-taketwo-strauss-zelnick/</link><pubDate>Sat, 01 Jun 2024 23:03:32 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/save-ksp2-an-open-letter-to-taketwo-strauss-zelnick/</guid><description>&lt;p>Dear Mr. Zelnick,&lt;/p>
&lt;p>I hope this letter finds you well. I am writing to you as a dedicated member of the Kerbal Space Program (KSP) community, a group that shares an unparalleled passion for space exploration and engineering, and whose enthusiasm for the franchise has only grown since the original game’s release. I am deeply concerned about the recent decision to cancel Kerbal Space Program 2 (KSP2), a game with enormous potential that deserves another chance to succeed under the right circumstances.&lt;/p>
&lt;p>Kerbal Space Program has cultivated a large and dedicated community that spans across all ages, particularly inspiring young minds to delve into the wonders of space travel and engineering. The game&amp;rsquo;s unique blend of education and entertainment has not only provided countless hours of enjoyment but has also sparked an interest in STEM fields for many children and young adults. The cancellation of KSP2 is not just a loss for the fans but a missed opportunity to continue fostering this educational impact.&lt;/p>
&lt;p>The challenges KSP2 has faced since its announcement can largely be attributed to mismanagement by Take-Two and Private Division. Despite these setbacks, the core vision and potential of the game remain intact. The issues are not with the concept or the community&amp;rsquo;s enthusiasm but with the execution and oversight of the project. With the right management and development team, KSP2 can overcome its current hurdles and achieve the greatness that the community knows it is capable of.&lt;/p>
&lt;p>Reconsidering the cancellation of KSP2 and investing in proper management and resources will not only save the game but also reinforce Take-Two&amp;rsquo;s commitment to its community and the educational value it brings. A revitalized KSP2 can reignite the interest of the next generation in space exploration and engineering, aligning perfectly with the growing global focus on these fields. The benefits of nurturing such interests extend beyond gaming, potentially leading to future innovations and advancements as inspired players pursue careers in STEM disciplines.&lt;/p>
&lt;p>The KSP community stands ready to support and contribute to the game&amp;rsquo;s success. Our passion and dedication are unwavering, and we believe that with the right leadership, KSP2 can become a landmark title that continues to inspire and educate.&lt;/p>
&lt;p>I kindly urge you to reconsider the decision to cancel Kerbal Space Program 2. Invest in the right team and management, and you will see not only a successful game but also a legacy that inspires future generations.&lt;/p>
&lt;p>Thank you for your time and consideration.&lt;/p>
&lt;p>Sincerely,&lt;/p>
&lt;p>The KSP Community&lt;/p>
&lt;hr>
&lt;p>I would urge all members of the KSP Community to send this to T2 CEO at the following address:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-text" data-lang="text">Mr. Strauss Zelnick
CEO, Take-Two Interactive
110 West 44th Street
New York, NY 10036
&lt;/code>&lt;/pre>&lt;/div>&lt;p>or via email to &lt;a href="mailto:strauss.zelnick@take2games.com">strauss.zelnick@take2games.com&lt;/a>&lt;/p></description></item><item><title>Why is AI Such a Polarising Subject? Does it Change Everything? Or Nothing?</title><link>https://www.darrenhorrocks.co.uk/why-ai-such-polarising-subject-does-it-change-everything/</link><pubDate>Wed, 15 May 2024 11:45:14 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-ai-such-polarising-subject-does-it-change-everything/</guid><description>&lt;p>AI seems to be a very polarising subject. On the whole, most people seem to be in one of two camps: &lt;strong>&amp;ldquo;AI IS THE FUTURE OF EVERYTHING!&amp;quot;&lt;/strong> or &lt;strong>&amp;ldquo;AI IS TERRIBLE AT EVERYTHING! IT&amp;rsquo;S NOT INTELLIGENT AT ALL&amp;rdquo;&lt;/strong> (caps intended).&lt;/p>
&lt;p>The polarisation around AI stems from multiple factors, including misunderstandings, exaggerated expectations, fear of the unknown, and legitimate concerns about the implications of AI on society.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Exaggerated Expectations:&lt;/strong> The media often sensationalises AI advancements, describing them as either utopian solutions to all our problems or dystopian threats to humanity. This leads to unrealistic expectations on one hand and unwarranted fear on the other.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Misunderstandings about AI:&lt;/strong> Many people have a limited understanding of what AI actually is and what it can do. This lack of understanding can lead to either overestimating AI capabilities (thinking it can do everything a human can) or underestimating them (believing it&amp;rsquo;s incapable of true intelligence).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Fear of Job Displacement:&lt;/strong> Automation powered by AI can have the potential to disrupt industries and replace certain types of jobs. This fear of job loss contributes to the negative perception of AI, especially among those who feel threatened by it.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Ethical Concerns:&lt;/strong> AI raises important ethical questions, such as bias in algorithms, invasion of privacy, and the potential for misuse (e.g., in surveillance or autonomous weapons). These concerns rightly lead to skepticism and criticism of AI development and deployment.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cultural and Philosophical Perspectives:&lt;/strong> Some individuals have philosophical or cultural beliefs that shape their views on AI. For example, those who view human intelligence as unique and sacred may be more skeptical of AI&amp;rsquo;s capabilities, while others who see technology as the path to progress may be more optimistic.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Experience and Exposure:&lt;/strong> Personal experiences with AI technology can greatly influence one&amp;rsquo;s opinion. Positive experiences may lead to enthusiasm and optimism, while negative experiences can breed skepticism and distrust.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Media Portrayal:&lt;/strong> Depictions of AI in movies, literature, and other forms of media often reinforce stereotypes and misconceptions, shaping public perception in the process.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>In reality, AI is neither the savior of humanity nor its downfall. It&amp;rsquo;s a tool, like a hammer or like any other technology, with the potential for both positive and negative impacts depending on how it&amp;rsquo;s developed and used. And like a hammer, not every problem is a nail, and not every problem requires AI, and problems that are nails, may or may not require the AI hammer. Acknowledging and accepting this is crucial for having productive discussions about AI and its role in our future.&lt;/p></description></item><item><title>PirateSoftware is a Hypocrite - Helldivers 2 and Security</title><link>https://www.darrenhorrocks.co.uk/piratesoftware-hypocrite-helldivers-2-security/</link><pubDate>Fri, 03 May 2024 10:14:38 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/piratesoftware-hypocrite-helldivers-2-security/</guid><description>&lt;p>PirateSoftware on Twitch took a stand against kernel-level anti-cheat in Helldivers 2 but found a workaround by buying a multi-thousand dollar second PC and plays it on there as a way of pretending they have “worked around” the kernel-level anti-cheat requirement. They are now pretending that the PSN account requirement for Helldivers, which was a requirement from the start (but not enforced), is a step too far because of “security”.&lt;/p>
&lt;p>He tried to justify the second PC by being “a security specialist” and “I didn&amp;rsquo;t invite it onto my system”, but the problem is, by buying the game and installing it, and playing the game, you are inviting it onto your system. His stance against the game would have led many to not play it, or many to also attempt to buy second systems they didn&amp;rsquo;t need.&lt;/p>
&lt;p>He played the game for many hours on stream, gaining quite a lot of ad revenue and bit/subscriber revenue because of the game and its hype. All the time, continuing his fake protest against kernel-level anti-cheat.&lt;/p>
&lt;p>Today, Sony announced that the PSN account requirement would start to be enforced. The same PSN account requirement was required from the start but was not enforced due to technical issues at Sony. Sony stated that the requirement would be enforced in the future.&lt;/p>
&lt;p>PirateSoftware played anyway because it benefited them to play Helldivers on stream. Now that benefit has expired, because the hype has worn out, his morals have snapped back and PirateSoftware is now taking a stand against the game.&lt;/p>
&lt;p>PirateSoftware should either have taken a stand from the start and not played the game at all, or admit that they are doing what is best for themselves and their stream since being honest with the community is the best course of action.&lt;/p></description></item><item><title>Stop Using string.ToLowerInvariant() to Compare Strings. InvariantCulture Comparisons are Slow</title><link>https://www.darrenhorrocks.co.uk/stop-using-stringtolowerinvariant-to-compare-strings-invariantculture-slow/</link><pubDate>Thu, 07 Mar 2024 11:29:49 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/stop-using-stringtolowerinvariant-to-compare-strings-invariantculture-slow/</guid><description>&lt;p>A lot of people use &lt;code>ToLower()&lt;/code> or &lt;code>ToLowerInvariant()&lt;/code> to normalise their strings before comparing them with &lt;code>==&lt;/code>, STOP IT.&lt;/p>
&lt;p>This is very slow, and while that may not matter for 1 or 2 string comparasons, it mounts up in large projects.&lt;/p>
&lt;p>While there is a very small use case for using Invariant Culture, unless you are absolutely certain that you need it, and why you need it, then you do not need it.&lt;/p>
&lt;p>&lt;a href="https://github.com/bizzehdee/StringEqualsSpeedTests">These tests in github&lt;/a> aim to show what we should be using instead.&lt;/p>
&lt;p>Each test was ran 100,000,000 times, which is a realistic number of records that could be compared in a loop.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Test&lt;/th>
&lt;th>Time in ms&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>ToLowerInvariant&lt;/td>
&lt;td>2667 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ToUpperInvariant&lt;/td>
&lt;td>2302 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DOUBLE ToLowerInvariant&lt;/td>
&lt;td>3864 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Equals (NoOptions) (as a control test)&lt;/td>
&lt;td>426 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Equals (InvariantCultureIgnoreCase)&lt;/td>
&lt;td>14891 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Equals (CurrentCultureIgnoreCase)&lt;/td>
&lt;td>14605 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Equals (OrdinalIgnoreCase)&lt;/td>
&lt;td>1168 ms&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>As you can see, Equals with &lt;code>OrdinalIgnoreCase&lt;/code> is an order of magnitude faster than &lt;code>InvariantCultureIgnoreCase&lt;/code>, and about twice as fast (half as slow) as &lt;code>ToLowerInvariant&lt;/code>. Stop using Invariant unless you absolutely understand your use case for it.&lt;/p>
&lt;p>&lt;img src="https://raw.githubusercontent.com/bizzehdee/StringEqualsSpeedTests/master/output.png" alt="Example">&lt;/p></description></item><item><title>Respecting the Clock: Unveiling the Time-Wasting Pitfalls in Professional Software Development</title><link>https://www.darrenhorrocks.co.uk/respecting-clock-unveiling-timewasting-pitfalls-professional-software-development/</link><pubDate>Wed, 10 Jan 2024 11:00:47 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/respecting-clock-unveiling-timewasting-pitfalls-professional-software-development/</guid><description>&lt;p>In software development, where time is very expensive and efficiency is paramount, it&amp;rsquo;s crucial to recognize and address common practices that can lead to unnecessary time wastage. This article aims to shed light on the professional nature of software developers, emphasizing the need for respectful and time-efficient communication within established software companies.&lt;/p>
&lt;h2 id="professionals-not-children">Professionals, Not Children&lt;/h2>
&lt;p>The first and foremost point to establish is the professionalism of those working in software development, particularly in well-established companies. These individuals are seasoned experts, not children needing hand-holding. Recognizing their expertise and treating them accordingly is vital for fostering a productive work environment.&lt;/p>
&lt;h2 id="the-high-cost-of-time">The High Cost of Time&lt;/h2>
&lt;p>Time is a valuable resource, especially for well-paid professionals in the software industry. Acknowledging the expense of their time is crucial in ensuring that every meeting, interaction, and decision is optimized for efficiency.&lt;/p>
&lt;h2 id="the-sugar-coated-time-sink">The Sugar-Coated Time Sink&lt;/h2>
&lt;p>Meetings laden with unnecessary sugar coating not only distract from the main points but also waste precious time. Striking a balance between diplomacy and directness is essential in ensuring that communication is both respectful and time-efficient.&lt;/p>
&lt;h2 id="stories-vs-succinctness">Stories vs. Succinctness&lt;/h2>
&lt;p>While storytelling can be a powerful communication tool, there&amp;rsquo;s a fine line between engaging narratives and time-consuming tangents. Meetings that meander through lengthy stories before getting to the point can become significant time-wasters, hindering productivity.&lt;/p>
&lt;h2 id="email-worthy-matters">Email-Worthy Matters&lt;/h2>
&lt;p>Identifying when a meeting could have been an email is crucial to preventing time wastage. Respect for professionals' time involves recognizing when face-to-face discussions are truly necessary and when information could be disseminated more efficiently through written communication.&lt;/p>
&lt;h2 id="selling-without-overselling">Selling without Overselling&lt;/h2>
&lt;p>Product owners attempting to sell a product to their own staff must strike a delicate balance. Glossing over downsides and exaggerating benefits can lead to disillusionment among professionals who can see through the smoke and mirrors. Honesty and transparency are key to maintaining trust.&lt;/p>
&lt;h2 id="evidence-over-knee-jerk-defense">Evidence Over Knee-Jerk Defense&lt;/h2>
&lt;p>In the realm of software development, evidence-based decision-making is paramount. Knee-jerk defending of a poor approach, even when faced with compelling evidence against it, only serves to hinder progress. Encouraging an open-minded and evidence-driven culture is vital for the continual improvement of processes and methodologies.&lt;/p>
&lt;p>In conclusion, recognizing the professionalism, time value, and expertise of software developers in established companies is the cornerstone of efficient collaboration. By eliminating time-wasting practices such as sugar coating, meandering meetings, and ineffective communication, the software development industry can strive for increased productivity and continual improvement.&lt;/p></description></item><item><title>Why are Women so Underrepresented in Software Development?</title><link>https://www.darrenhorrocks.co.uk/why-are-women-so-underrepresented-software-development/</link><pubDate>Thu, 30 Nov 2023 09:33:04 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/why-are-women-so-underrepresented-software-development/</guid><description>&lt;p>I have to preface this with &amp;ldquo;I am a man, not a woman, this is an opinion based on what I have seen in 20+ years of working as a software developer in many different companies&amp;rdquo;.&lt;/p>
&lt;p>With that, software development has a noticeable gender gap, with women underrepresented in comparison to their male counterparts. This disparity is a multifaceted issue with roots in societal, educational, and workplace factors. In this article, we&amp;rsquo;ll explore the various reasons behind the underrepresentation of women in programming jobs and discuss potential solutions to bridge this gap.&lt;/p>
&lt;h2 id="societal-stereotypes">Societal Stereotypes&lt;/h2>
&lt;p>From a young age, societal stereotypes can shape perceptions of gender roles, influencing career choices. Stereotypes that suggest programming is a male-dominated field may discourage girls from pursuing interests in technology. Challenging and reshaping these stereotypes is crucial for fostering inclusivity.&lt;/p>
&lt;p>A possible &amp;ldquo;solution&amp;rdquo; (that would require a cultural shift) to this is challenging stereotypes through educational initiatives and awareness campaigns that showcase successful women in programming. Encourage media, schools, and parents to provide equal exposure to technology-related activities for both girls and boys.&lt;/p>
&lt;h2 id="educational-disparities">Educational Disparities&lt;/h2>
&lt;p>Access to quality computer science education is essential for nurturing an interest in programming. Disparities in educational opportunities and experiences can limit girls' exposure to coding and technology, hindering their path to programming careers. Efforts to provide equal access and encouragement in STEM education are key to addressing this issue.&lt;/p>
&lt;p>Again, combatting this would require a cultural and politically driven shift as we would need to implement policies that promote equal access to computer science education for all students. Create mentorship programs and extracurricular activities that specifically target and support girls interested in coding and technology.&lt;/p>
&lt;h2 id="lack-of-role-models">Lack of Role Models&lt;/h2>
&lt;p>Despite some of the most prominent programmers from history being women, I believe that the absence of visible female role models in programming can contribute to the underrepresentation of women. Highlighting successful women in the field can inspire and empower others, breaking down stereotypes and demonstrating that programming is a viable and rewarding career choice for women.&lt;/p>
&lt;p>In school, everybody learns about Alan Turing, and maybe Charles Babbage, and are taught who Bill Gates is, but why do we not get taught about Ada Lovelace, Grace Hopper or Margaret Hamilton?&lt;/p>
&lt;h2 id="workplace-culture">Workplace Culture&lt;/h2>
&lt;p>Creating an inclusive workplace culture is pivotal in attracting and retaining women in programming jobs. Companies must actively work to eliminate gender bias, promote diversity and inclusion, and foster an environment where all employees feel valued and respected.&lt;/p>
&lt;p>This will only be solved with the points before this point being addressed, the more women that enter the workplace, the more that the culture will shift away from the &amp;ldquo;nerdy boys&amp;rdquo; stereotype of programming jobs, allowing the whole workplace culture to be more suitable for anybody.&lt;/p>
&lt;h2 id="implicit-bias">Implicit Bias&lt;/h2>
&lt;p>Unconscious biases can influence hiring decisions and career advancement opportunities. Addressing implicit biases, both on an individual and organizational level, is crucial for ensuring fair and equal treatment in the recruitment process.&lt;/p>
&lt;h2 id="work-life-balance-challenges">Work-Life Balance Challenges&lt;/h2>
&lt;p>Perceptions of programming jobs as demanding and incompatible with work-life balance can deter women, especially those concerned about balancing career and family responsibilities. Promoting flexible work arrangements and emphasizing the importance of work-life balance can make programming careers more accessible.&lt;/p>
&lt;p>This was recently pushed dramatically in the correct direction with the huge shift towards &amp;ldquo;Work From Home&amp;rdquo; culture, that many software development jobs are keeping in place, which helps everybody, not just women.&lt;/p>
&lt;h2 id="lack-of-networking-opportunities">Lack of Networking Opportunities&lt;/h2>
&lt;p>Networking plays a vital role in career advancement. Creating networking and mentorship opportunities specifically designed to support women in programming can help build connections and facilitate professional growth.&lt;/p>
&lt;h2 id="unconscious-biases-in-hiring">Unconscious Biases in Hiring&lt;/h2>
&lt;p>Examining and adjusting hiring practices is essential to eliminate biases that may inadvertently disadvantage women. Using gender-neutral language in job descriptions, broadening criteria, and ensuring diverse hiring panels can contribute to a more equitable hiring process.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Addressing the underrepresentation of women in programming requires a collective effort from educators, industry leaders, and policymakers. By challenging stereotypes, promoting diversity in education, creating inclusive workplace cultures, and addressing unconscious biases, we can pave the way for a more gender-balanced and thriving programming community. It&amp;rsquo;s time to break down barriers and ensure that women have equal opportunities to contribute their skills and innovation to the dynamic world of programming.&lt;/p></description></item><item><title>Whats New in ML.NET 3.0</title><link>https://www.darrenhorrocks.co.uk/whats-new-mlnet-30/</link><pubDate>Tue, 28 Nov 2023 09:23:19 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/whats-new-mlnet-30/</guid><description>&lt;p>ML.NET is an open-source, cross-platform machine learning framework for .NET developers, offering exciting capabilities in deep learning, data processing, and more. Given that Microsoft &lt;a href="https://devblogs.microsoft.com/dotnet/announcing-ml-net-3-0/">recently announced ML.NET 3.0&lt;/a>, let&amp;rsquo;s take a closer look at what ML.NET 3.0 has in store.&lt;/p>
&lt;h3 id="deep-learning-takes-center-stage">Deep Learning Takes Center Stage&lt;/h3>
&lt;p>In ML.NET 3.0, deep learning scenarios have been significantly expanded. The framework now boasts new capabilities in Object Detection, Named Entity Recognition (NER), and Question Answering (QA). These advancements are made possible through seamless integrations with TorchSharp and ONNX models. The integration with LightGBM has also been updated to the latest version, ensuring that you&amp;rsquo;re on the cutting edge of deep learning possibilities.&lt;/p>
&lt;h3 id="object-detection-unleashed">Object Detection Unleashed&lt;/h3>
&lt;p>Object detection, a crucial aspect of computer vision, is now seamlessly integrated into ML.NET 3.0. This functionality allows for more granular image classification, locating and categorizing entities within images. Leveraging TorchSharp-powered Object Detection APIs, ML.NET enables you to work with the latest techniques from Microsoft Research, backed by a state-of-the-art Transformer-based neural network architecture.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Sample code for Object Detection
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> chain = &lt;span style="color:#66d9ef">new&lt;/span> EstimatorChain&amp;lt;ITransformer&amp;gt;();
&lt;span style="color:#75715e">// ... (code for data processing and model training)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> metrics = ML.MulticlassClassification.EvaluateObjectDetection(
idv, idv.Schema[&lt;span style="color:#ae81ff">2&lt;/span>], idv.Schema[boundingBoxColumnName], idv.Schema[predictedLabelColumnName],
idv.Schema[predictedBoundingBoxColumnName], idv.Schema[scoreColumnName]
);
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="named-entity-recognition-and-question-answering">Named Entity Recognition and Question Answering&lt;/h3>
&lt;p>Natural Language Processing (NLP) is a common need in software, and ML.NET 3.0 meets this demand head-on. NER and QA scenarios are unlocked in this release, building upon the existing TorchSharp RoBERTa text classification features introduced in ML.NET 2.0. The trainers for both NER and QA are bundled within the Microsoft.ML.TorchSharp 3.0.0 package.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Sample code for QA trainer
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> chain = &lt;span style="color:#66d9ef">new&lt;/span> EstimatorChain&amp;lt;ITransformer&amp;gt;();
&lt;span style="color:#66d9ef">var&lt;/span> estimatorQA = chain.Append(mlContext.MulticlassClassification.Trainers.QuestionAnswer(
&lt;span style="color:#75715e">// ... (parameters for QA training)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>));
&lt;span style="color:#75715e">// Sample code for NER trainer
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> estimatorNER = chain.Append(mlContext.Transforms.Conversion.MapValueToKey(&lt;span style="color:#e6db74">&amp;#34;Label&amp;#34;&lt;/span>, keyData))
.Append(mlContext.MulticlassClassification.Trainers.NameEntityRecognition(
&lt;span style="color:#75715e">// ... (parameters for NER training)
&lt;/span>&lt;span style="color:#75715e">&lt;/span> ))
.Append(mlContext.Transforms.Conversion.MapKeyToValue(outputColumn));
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="intel-onedal-training-acceleration">Intel oneDAL Training Acceleration&lt;/h3>
&lt;p>ML.NET 3.0 introduces training hardware acceleration powered by Intel oneDAL (Intel oneAPI Data Analytics Library). This library optimizes data analysis by providing highly optimized algorithmic building blocks, utilizing SIMD extensions in 64-bit architectures found in both Intel and AMD CPUs.&lt;/p>
&lt;h3 id="automated-machine-learning-automl-empowerment">Automated Machine Learning (AutoML) Empowerment&lt;/h3>
&lt;p>AutoML experiences in ML.NET get a boost in version 3.0. The AutoML Sweeper now supports Sentence Similarity, QA, and Object Detection. Continuous resource monitoring is introduced to control long-running experiments and avoid crashes.&lt;/p>
&lt;h3 id="dataframe-revamped">DataFrame Revamped&lt;/h3>
&lt;p>The release includes a plethora of updates to DataFrame, enhancing its capabilities. These updates, contributed by the community, include support for IDataView &amp;lt;-&amp;gt; DataFrame conversions with String and VBuffer column types. Increased data storage capacity, recognition of Apache Arrow Date64 column data, and expanded data loading scenarios from and to SQL databases are some notable improvements.&lt;/p>
&lt;h3 id="tensor-primitives-integration">Tensor Primitives Integration&lt;/h3>
&lt;p>ML.NET 3.0 seamlessly integrates with Tensor Primitives (System.Numerics.Tensors), introducing support for tensor operations and bringing about notable performance improvements. Benchmark results targeting .NET 8 showcase the efficiency gains achieved through this integration.&lt;/p>
&lt;p>ML.NET 3.0 is a game-changer for .NET developers diving into machine learning. With enhanced deep learning capabilities, streamlined data processing, and integration with cutting-edge technologies, it opens up a world of possibilities. Whether you&amp;rsquo;re into computer vision, natural language processing, or automated machine learning, ML.NET 3.0 has something for everyone. Dive into the release notes for a comprehensive view of all the updates and start exploring the future of machine learning with ML.NET!&lt;/p></description></item><item><title>Mastering Backend Development in C#: 5 Essential Skills</title><link>https://www.darrenhorrocks.co.uk/mastering-backend-development-c-5-essential-skills/</link><pubDate>Fri, 24 Nov 2023 21:15:20 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/mastering-backend-development-c-5-essential-skills/</guid><description>&lt;p>Backend development in C# involves more than just writing code. To become a proficient backend developer, one must master a combination of programming language fundamentals and specific tools and frameworks. In this article, we&amp;rsquo;ll explore five essential skills that every backend C# developer should have, and we&amp;rsquo;ll delve into why each skill is crucial for success.&lt;/p>
&lt;h2 id="1-c-language-proficiency">1. C# Language Proficiency&lt;/h2>
&lt;h3 id="generics">Generics:&lt;/h3>
&lt;p>Generics in C# provide a powerful way to create reusable and type-safe components. This is essential for writing flexible and maintainable code. By defining classes and methods with generics, developers can design components that work with a variety of data types, promoting code reusability.&lt;/p>
&lt;h3 id="linq-language-integrated-query">LINQ (Language Integrated Query):&lt;/h3>
&lt;p>LINQ is a feature that allows developers to write queries directly within C# code. This not only simplifies the syntax but also enhances code readability. Proficiency in LINQ is crucial for efficient data manipulation, making it an indispensable skill for any C# developer.&lt;/p>
&lt;h3 id="asynchronous-programming">Asynchronous Programming:&lt;/h3>
&lt;p>Asynchronous programming with &lt;code>async&lt;/code> and &lt;code>await&lt;/code> is a game-changer for backend developers. It allows for the creation of responsive applications by handling time-consuming operations, such as I/O tasks, without blocking the main thread. This skill is paramount for building scalable and high-performance systems.&lt;/p>
&lt;h2 id="2-aspnet-core">2. ASP.NET Core&lt;/h2>
&lt;h3 id="middleware">Middleware:&lt;/h3>
&lt;p>Middleware components in ASP.NET Core enable developers to modularize application logic. This promotes a separation of concerns, making the codebase more maintainable and extensible. Understanding how to leverage middleware is fundamental for crafting robust and scalable web applications.&lt;/p>
&lt;h3 id="routing">Routing:&lt;/h3>
&lt;p>Efficient routing is the backbone of any web application. ASP.NET Core&amp;rsquo;s routing system directs incoming requests to the appropriate controllers and actions. Mastery of routing is crucial for building RESTful APIs and MVC applications, ensuring that requests are processed seamlessly.&lt;/p>
&lt;h3 id="dependency-injection-in-aspnet-core">Dependency Injection in ASP.NET Core:&lt;/h3>
&lt;p>ASP.NET Core comes with a built-in dependency injection container. Proficiency in dependency injection allows developers to manage dependencies, making their codebase more modular, testable, and easier to maintain. It&amp;rsquo;s a key aspect of building loosely coupled and scalable systems.&lt;/p>
&lt;h2 id="3-entity-framework-core">3. Entity Framework Core&lt;/h2>
&lt;h3 id="code-first-development">Code-First Development:&lt;/h3>
&lt;p>Entity Framework Core supports a code-first approach, allowing developers to define data models in code. This approach aligns development with the codebase, making it easier to manage and version database schemas. Code-first development streamlines the database integration process.&lt;/p>
&lt;h3 id="migrations">Migrations:&lt;/h3>
&lt;p>Database schema evolution is a common challenge in software development. Entity Framework Core&amp;rsquo;s migration feature automates this process, making it easier to manage changes over time. Developers can create, apply, and roll back migrations, ensuring consistency between the application and the database.&lt;/p>
&lt;h3 id="querying-and-crud-operations">Querying and CRUD Operations:&lt;/h3>
&lt;p>Entity Framework Core simplifies database interactions by providing a high-level abstraction for CRUD operations. Developers can focus on application logic rather than dealing with low-level database access. Proficiency in querying and CRUD operations is vital for efficient data management.&lt;/p>
&lt;h2 id="4-dependency-injection">4. Dependency Injection&lt;/h2>
&lt;h3 id="constructor-injection">Constructor Injection:&lt;/h3>
&lt;p>Constructor injection is a fundamental principle of dependency injection. It promotes modularity and testability by explicitly declaring and injecting dependencies. This leads to code that is more transparent, maintainable, and easier to test.&lt;/p>
&lt;h3 id="lifetime-management">Lifetime Management:&lt;/h3>
&lt;p>Understanding the lifetime of dependencies is crucial for managing resources effectively. ASP.NET Core&amp;rsquo;s dependency injection container offers different lifetimes (transient, scoped, singleton) for managing how instances of dependencies are created and disposed. Proper lifetime management ensures optimal resource usage.&lt;/p>
&lt;h2 id="5-restful-api-design">5. RESTful API Design&lt;/h2>
&lt;h3 id="http-methods">HTTP Methods:&lt;/h3>
&lt;p>RESTful API design relies on proper usage of HTTP methods. Each method has a specific purpose (GET for retrieval, POST for creation, etc.), providing a standardized and intuitive way to perform operations on resources. Following RESTful principles improves the design and usability of APIs.&lt;/p>
&lt;h3 id="status-codes">Status Codes:&lt;/h3>
&lt;p>HTTP status codes communicate the outcome of API requests. This allows clients to understand the success or failure of their actions. Clear and consistent use of status codes is essential for a positive user experience and effective error handling.&lt;/p>
&lt;h3 id="versioning-and-documentation">Versioning and Documentation:&lt;/h3>
&lt;p>API versioning allows for gradual updates and backward compatibility. Clear documentation is crucial for developers using the API, providing insights into endpoints, request/response formats, and authentication. Tools like Swagger/OpenAPI can assist in creating and maintaining comprehensive API documentation.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Mastering these five essential skills is key to becoming a successful backend C# developer. Proficiency in the C# language, ASP.NET Core, Entity Framework Core, dependency injection, and RESTful API design will empower you to build scalable, maintainable, and high-performance backend systems. Remember, continuous learning and staying updated with industry trends are vital for long-term success in the dynamic field of backend development. Happy coding!&lt;/p></description></item><item><title>.NET Chiseled Containers: A Revolution in Deployment Efficiency</title><link>https://www.darrenhorrocks.co.uk/unveiling-net-chiseled-containers-revolution-deployment-efficiency/</link><pubDate>Tue, 21 Nov 2023 15:02:35 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/unveiling-net-chiseled-containers-revolution-deployment-efficiency/</guid><description>&lt;p>In a groundbreaking announcement, Canonical and Microsoft have declared the general availability of &amp;ldquo;.NET Chiseled Ubuntu container images&amp;rdquo; for production use with .NET 6, 7, and 8. This release is the culmination of a year-long partnership and design collaboration between the two tech giants, aimed at optimizing containerized applications for size, security, and efficiency.&lt;/p>
&lt;h2 id="what-are-chiseled-containers">What are Chiseled Containers?&lt;/h2>
&lt;p>Chiseled containers represent a paradigm shift in container image design philosophy. Traditional container images often include a multitude of unnecessary components, leading to larger sizes and increased security vulnerabilities. The core concept behind chiseled containers is to meticulously trim away non-essential elements, creating a lean and secure deployment vehicle for cloud applications.&lt;/p>
&lt;h2 id="key-features-of-chiseled-containers">Key Features of Chiseled Containers&lt;/h2>
&lt;h3 id="1-size-and-security-optimization">1. Size and Security Optimization&lt;/h3>
&lt;p>One of the primary challenges in managing container images is handling Common Vulnerabilities and Exposures (CVEs). Chiseled containers address this issue by significantly reducing the number of components, thereby minimizing the attack surface. A comparative analysis using anchore/syft commands demonstrates a stark difference in the number of components between chiseled and non-chiseled images.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ docker run --rm anchore/syft mcr.microsoft.com/dotnet/runtime:8.0 | grep deb | wc -l
&lt;span style="color:#ae81ff">92&lt;/span>
$ docker run --rm anchore/syft mcr.microsoft.com/dotnet/runtime:8.0-jammy-chiseled | grep deb | wc -l
&lt;span style="color:#ae81ff">7&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The reduction in component count enhances the security posture and simplifies CVE management.&lt;/p>
&lt;h3 id="2-smaller-image-footprint">2. Smaller Image Footprint&lt;/h3>
&lt;p>Chiseled containers boast a significantly smaller footprint compared to their non-chiseled counterparts. For instance, the uncompressed size of the .NET aspnet image is approximately 110MB for the chiseled variant, rivaling the size of Alpine, a well-known lightweight base image.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">$ docker images --format &lt;span style="color:#e6db74">&amp;#34;table {{.Repository}}\t{{.Tag}}\t{{.Size}}&amp;#34;&lt;/span> | grep mcr.microsoft.com/dotnet/aspnet
mcr.microsoft.com/dotnet/aspnet 8.0-jammy-chiseled 110MB
mcr.microsoft.com/dotnet/aspnet 8.0-alpine 112MB
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This reduction in size contributes to faster image pull times and optimized resource utilization.&lt;/p>
&lt;h3 id="3-distroless-form-factor">3. Distroless Form Factor&lt;/h3>
&lt;p>Chiseled containers embody the distroless form factor, emphasizing only the essential components required for application execution. Unlike traditional Linux distributions, they exclude unnecessary tools and package managers, leading to a more secure runtime environment.&lt;/p>
&lt;h2 id="how-to-use-net-chiseled-containers">How to Use .NET Chiseled Containers&lt;/h2>
&lt;h3 id="1-tag-and-versioning">1. Tag and Versioning&lt;/h3>
&lt;p>Chiseled container images are available in the container repositories with the following tag: &lt;code>8.0-jammy-chiseled&lt;/code>. The version number may vary for .NET 6 and 7 variants.&lt;/p>
&lt;h3 id="2-image-selection">2. Image Selection&lt;/h3>
&lt;p>Selecting the appropriate image type depends on the deployment requirements:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Framework-Dependent Deployment:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Maximum layer sharing for faster build times.&lt;/li>
&lt;li>Recommended for hosting multiple .NET apps on the same machine.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Self-Contained Apps:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Significantly smaller image size but limited layer sharing.&lt;/li>
&lt;li>Suitable for scenarios where size optimization is a priority.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Native AOT (Ahead-of-Time) Apps:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Further reduces image size, particularly for console apps and services.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="3-security-considerations">3. Security Considerations&lt;/h3>
&lt;p>Chiseled containers enhance security by excluding shells, package managers, and minimizing the attack surface. Furthermore, these containers are shipped as non-root, limiting the potential impact of malicious activities.&lt;/p>
&lt;h2 id="adoption-and-future-outlook">Adoption and Future Outlook&lt;/h2>
&lt;p>Chiseled containers represent a significant advancement in the .NET container image portfolio. While .NET 8 users are the most immediate beneficiaries, users of Ubuntu and Debian stand to gain substantial size savings by adopting chiseled containers.&lt;/p>
&lt;p>Microsoft and Canonical recommend careful consideration of chiseled containers, emphasizing their benefits in terms of reduced image size, enhanced security, and simplified CVE management. The collaboration between the two companies signals a commitment to ongoing support and potential advancements in containerization.&lt;/p>
&lt;p>In conclusion, the era of chiseled containers has arrived, promising a revolution in how developers approach containerized application deployment. As the adoption of chiseled containers grows, it is anticipated that other developer ecosystems, such as Java, Python, and Node.js, will explore and offer their own versions of chiseled images. The future of containerization looks more efficient, secure, and streamlined with the introduction of .NET chiseled containers.&lt;/p></description></item><item><title>Asynchronous Programming in C#: A Practical Guide</title><link>https://www.darrenhorrocks.co.uk/asynchronous-programming-c-practical-guide/</link><pubDate>Tue, 21 Nov 2023 08:45:42 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/asynchronous-programming-c-practical-guide/</guid><description>&lt;p>As applications grow in complexity, the need for responsive and efficient code becomes paramount. Asynchronous programming in C# offers a powerful solution to handling time-consuming operations without blocking the main thread. This practical guide aims to demystify asynchronous programming in C#, providing a comprehensive overview and practical examples to help you harness the benefits of asynchronous operations.&lt;/p>
&lt;h2 id="1-understanding-asynchronous-programming">1. Understanding Asynchronous Programming&lt;/h2>
&lt;ul>
&lt;li>Explanation of synchronous vs. asynchronous operations.&lt;/li>
&lt;li>The importance of asynchronous programming for responsiveness and scalability.&lt;/li>
&lt;li>Introduction to the &lt;code>async&lt;/code> and &lt;code>await&lt;/code> keywords.&lt;/li>
&lt;/ul>
&lt;h2 id="2-basics-of-asynchronous-methods">2. Basics of Asynchronous Methods&lt;/h2>
&lt;h3 id="a-async-methods">a. Async Methods&lt;/h3>
&lt;ul>
&lt;li>Creating asynchronous methods using the &lt;code>async&lt;/code> keyword.&lt;/li>
&lt;li>Returning &lt;code>Task&lt;/code> or &lt;code>Task&amp;lt;T&amp;gt;&lt;/code> from asynchronous methods.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>&amp;gt; DownloadDataAsync()
{
&lt;span style="color:#75715e">// Asynchronous operation
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> SomeApi.DownloadDataAsync();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="b-awaitable-types">b. Awaitable Types&lt;/h3>
&lt;ul>
&lt;li>Understanding types that can be awaited (e.g., &lt;code>Task&lt;/code>, &lt;code>Task&amp;lt;T&amp;gt;&lt;/code>, &lt;code>ValueTask&amp;lt;T&amp;gt;&lt;/code>).&lt;/li>
&lt;li>Handling asynchronous operations without blocking the thread.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task PerformAsyncOperation()
{
&lt;span style="color:#75715e">// Asynchronously wait for the result
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> result = &lt;span style="color:#66d9ef">await&lt;/span> SomeClass.PerformOperationAsync();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="3-asynchronous-patterns">3. Asynchronous Patterns&lt;/h2>
&lt;h3 id="a-async-and-await-best-practices">a. Async and Await Best Practices&lt;/h3>
&lt;ul>
&lt;li>Best practices for using &lt;code>async&lt;/code> and &lt;code>await&lt;/code> keywords.&lt;/li>
&lt;li>Avoiding blocking calls in asynchronous methods.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; CalculateSumAsync()
{
&lt;span style="color:#66d9ef">var&lt;/span> result1 = &lt;span style="color:#66d9ef">await&lt;/span> Task.Run(() =&amp;gt; ComputePart1());
&lt;span style="color:#66d9ef">var&lt;/span> result2 = &lt;span style="color:#66d9ef">await&lt;/span> Task.Run(() =&amp;gt; ComputePart2());
&lt;span style="color:#66d9ef">return&lt;/span> result1 + result2;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="b-taskwhenall-and-taskwhenany">b. Task.WhenAll and Task.WhenAny&lt;/h3>
&lt;ul>
&lt;li>Utilizing &lt;code>Task.WhenAll&lt;/code> for parallel asynchronous operations.&lt;/li>
&lt;li>Using &lt;code>Task.WhenAny&lt;/code> for scenarios where the first completed task is needed.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>[]&amp;gt; DownloadAllDataAsync()
{
&lt;span style="color:#66d9ef">var&lt;/span> downloadTasks = urls.Select(url =&amp;gt; DownloadDataAsync(url));
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> Task.WhenAll(downloadTasks);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="4-exception-handling-in-asynchronous-code">4. Exception Handling in Asynchronous Code&lt;/h2>
&lt;ul>
&lt;li>Handling exceptions in asynchronous code.&lt;/li>
&lt;li>Using &lt;code>try&lt;/code>, &lt;code>catch&lt;/code>, and &lt;code>finally&lt;/code> with asynchronous methods.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; DivideAsync(&lt;span style="color:#66d9ef">int&lt;/span> a, &lt;span style="color:#66d9ef">int&lt;/span> b)
{
&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> Task.Run(() =&amp;gt; a / b);
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DivideByZeroException ex)
{
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Division by zero: {ex.Message}&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="5-asynchronous-file-io-and-network-operations">5. Asynchronous File I/O and Network Operations&lt;/h2>
&lt;h3 id="a-reading-and-writing-files-asynchronously">a. Reading and Writing Files Asynchronously&lt;/h3>
&lt;ul>
&lt;li>Performing file I/O operations asynchronously.&lt;/li>
&lt;li>Using &lt;code>StreamReader&lt;/code>, &lt;code>StreamWriter&lt;/code>, and &lt;code>FileStream&lt;/code> asynchronously.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task ReadWriteFileAsync(&lt;span style="color:#66d9ef">string&lt;/span> inputFile, &lt;span style="color:#66d9ef">string&lt;/span> outputFile)
{
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StreamReader(inputFile))
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> writer = &lt;span style="color:#66d9ef">new&lt;/span> StreamWriter(outputFile))
{
&lt;span style="color:#66d9ef">var&lt;/span> content = &lt;span style="color:#66d9ef">await&lt;/span> reader.ReadToEndAsync();
&lt;span style="color:#66d9ef">await&lt;/span> writer.WriteAsync(content);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="b-making-asynchronous-web-requests">b. Making Asynchronous Web Requests&lt;/h3>
&lt;ul>
&lt;li>Utilizing asynchronous HTTP requests using &lt;code>HttpClient&lt;/code>.&lt;/li>
&lt;li>Handling responses asynchronously.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>&amp;gt; DownloadDataAsync(&lt;span style="color:#66d9ef">string&lt;/span> url)
{
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> client = &lt;span style="color:#66d9ef">new&lt;/span> HttpClient())
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">await&lt;/span> client.GetStringAsync(url);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="6-synchronization-context-and-taskrun">6. Synchronization Context and Task.Run&lt;/h2>
&lt;ul>
&lt;li>Understanding the Synchronization Context and its impact on asynchronous code.&lt;/li>
&lt;li>Using &lt;code>Task.Run&lt;/code> to offload CPU-bound work to a separate thread.&lt;/li>
&lt;li>&lt;strong>C# Example:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">async&lt;/span> Task RunTaskOnThreadPool()
{
&lt;span style="color:#66d9ef">await&lt;/span> Task.Run(() =&amp;gt;
{
&lt;span style="color:#75715e">// Code to run on a separate thread
&lt;/span>&lt;span style="color:#75715e">&lt;/span> });
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Asynchronous programming in C# is a powerful tool for improving the performance and responsiveness of your applications. This guide has covered the fundamentals of asynchronous programming, from basic syntax to best practices and real-world examples. By incorporating asynchronous techniques into your codebase, you can create more efficient and scalable applications that meet the demands of modern software development.&lt;/p></description></item><item><title>C# Design Patterns: A Practical Guide</title><link>https://www.darrenhorrocks.co.uk/c-sharp-design-patterns-practical-guide/</link><pubDate>Mon, 20 Nov 2023 20:25:34 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/c-sharp-design-patterns-practical-guide/</guid><description>&lt;p>Design patterns offer reusable solutions to common problems in software design, providing a framework for creating flexible and maintainable code. In C#, understanding and implementing design patterns can significantly enhance the structure and efficiency of your applications. This practical guide will explore key design patterns in C#, providing real-world examples to illustrate their application.&lt;/p>
&lt;h2 id="1-understanding-design-patterns">1. Understanding Design Patterns&lt;/h2>
&lt;ul>
&lt;li>Design patterns are proven solutions to recurring design problems.&lt;/li>
&lt;li>Three main categories: Creational, Structural, and Behavioral.&lt;/li>
&lt;/ul>
&lt;h2 id="2-creational-design-patterns">2. Creational Design Patterns&lt;/h2>
&lt;h3 id="a-singleton-pattern">a. Singleton Pattern&lt;/h3>
&lt;ul>
&lt;li>Ensures a class has only one instance and provides a global point to that instance.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Singleton&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Singleton &lt;span style="color:#ae81ff">_&lt;/span>instance;
&lt;span style="color:#66d9ef">private&lt;/span> Singleton() { }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Singleton Instance
{
&lt;span style="color:#66d9ef">get&lt;/span>
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#ae81ff">_&lt;/span>instance == &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#ae81ff">_&lt;/span>instance = &lt;span style="color:#66d9ef">new&lt;/span> Singleton();
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">_&lt;/span>instance;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="b-factory-method-pattern">b. Factory Method Pattern&lt;/h3>
&lt;ul>
&lt;li>Defines an interface for creating an object but lets subclasses alter the type of objects that will be created.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IProduct
{
&lt;span style="color:#66d9ef">void&lt;/span> Produce();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteProduct&lt;/span> : IProduct
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Produce() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Producing Concrete Product&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Creator&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> IProduct FactoryMethod();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteCreator&lt;/span> : Creator
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> IProduct FactoryMethod() =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> ConcreteProduct();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="c-abstract-factory-pattern">c. Abstract Factory Pattern&lt;/h3>
&lt;ul>
&lt;li>Provides an interface for creating families of related or dependent objects without specifying their concrete classes.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IAbstractFactory
{
IProductA CreateProductA();
IProductB CreateProductB();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IProductA { }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IProductB { }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteFactory&lt;/span> : IAbstractFactory
{
&lt;span style="color:#66d9ef">public&lt;/span> IProductA CreateProductA() =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> ConcreteProductA();
&lt;span style="color:#66d9ef">public&lt;/span> IProductB CreateProductB() =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> ConcreteProductB();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteProductA&lt;/span> : IProductA { }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteProductB&lt;/span> : IProductB { }
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="3-structural-design-patterns">3. Structural Design Patterns&lt;/h2>
&lt;h3 id="a-adapter-pattern">a. Adapter Pattern&lt;/h3>
&lt;ul>
&lt;li>Allows incompatible interfaces to work together.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> ITarget
{
&lt;span style="color:#66d9ef">void&lt;/span> Request();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Adaptee&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SpecificRequest() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Specific Request&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Adapter&lt;/span> : ITarget
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Adaptee &lt;span style="color:#ae81ff">_&lt;/span>adaptee;
&lt;span style="color:#66d9ef">public&lt;/span> Adapter(Adaptee adaptee) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>adaptee = adaptee;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Request() =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>adaptee.SpecificRequest();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="b-decorator-pattern">b. Decorator Pattern&lt;/h3>
&lt;ul>
&lt;li>Attaches additional responsibilities to an object dynamically.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IComponent
{
&lt;span style="color:#66d9ef">void&lt;/span> Operation();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteComponent&lt;/span> : IComponent
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Operation() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Concrete Component&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Decorator&lt;/span> : IComponent
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> IComponent &lt;span style="color:#ae81ff">_&lt;/span>component;
&lt;span style="color:#66d9ef">public&lt;/span> Decorator(IComponent component) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>component = component;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Operation()
{
&lt;span style="color:#ae81ff">_&lt;/span>component.Operation();
Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34; + Decorator&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="c-composite-pattern">c. Composite Pattern&lt;/h3>
&lt;ul>
&lt;li>Composes objects into tree structures to represent part-whole hierarchies.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Component&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Operation();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Leaf&lt;/span> : Component
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Operation() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Leaf Operation&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Composite&lt;/span> : Component
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> List&amp;lt;Component&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>children = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;Component&amp;gt;();
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Add(Component component) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>children.Add(component);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Operation()
{
Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Composite Operation&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> child &lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#ae81ff">_&lt;/span>children)
{
child.Operation();
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="4-behavioral-design-patterns">4. Behavioral Design Patterns&lt;/h2>
&lt;h3 id="a-observer-pattern">a. Observer Pattern&lt;/h3>
&lt;ul>
&lt;li>Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IObserver
{
&lt;span style="color:#66d9ef">void&lt;/span> Update(&lt;span style="color:#66d9ef">string&lt;/span> message);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteObserver&lt;/span> : IObserver
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Update(&lt;span style="color:#66d9ef">string&lt;/span> message) =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Received update: {message}&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Subject&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> List&amp;lt;IObserver&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>observers = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;IObserver&amp;gt;();
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Attach(IObserver observer) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>observers.Add(observer);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> NotifyObservers(&lt;span style="color:#66d9ef">string&lt;/span> message)
{
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> observer &lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#ae81ff">_&lt;/span>observers)
{
observer.Update(message);
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="b-strategy-pattern">b. Strategy Pattern&lt;/h3>
&lt;ul>
&lt;li>Defines a family of algorithms, encapsulates each one, and makes them interchangeable.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> IStrategy
{
&lt;span style="color:#66d9ef">void&lt;/span> Execute();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteStrategyA&lt;/span> : IStrategy
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Execute() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Executing Strategy A&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteStrategyB&lt;/span> : IStrategy
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Execute() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Executing Strategy B&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Context&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> IStrategy &lt;span style="color:#ae81ff">_&lt;/span>strategy;
&lt;span style="color:#66d9ef">public&lt;/span> Context(IStrategy strategy) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>strategy = strategy;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SetStrategy(IStrategy strategy) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>strategy = strategy;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> ExecuteStrategy() =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>strategy.Execute();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h3 id="c-command-pattern">c. Command Pattern&lt;/h3>
&lt;ul>
&lt;li>Encapsulates a request as an object, thereby allowing for parameterization of clients with different requests, queuing of requests, and logging of the requests.&lt;/li>
&lt;li>&lt;strong>C# Implementation:&lt;/strong>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">interface&lt;/span> ICommand
{
&lt;span style="color:#66d9ef">void&lt;/span> Execute();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ConcreteCommand&lt;/span> : ICommand
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Receiver &lt;span style="color:#ae81ff">_&lt;/span>receiver;
&lt;span style="color:#66d9ef">public&lt;/span> ConcreteCommand(Receiver receiver) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>receiver = receiver;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Execute() =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>receiver.Action();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Receiver&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Action() =&amp;gt; Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Receiver Action&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Invoker&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> ICommand &lt;span style="color:#ae81ff">_&lt;/span>command;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SetCommand(ICommand command) =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>command = command;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> ExecuteCommand() =&amp;gt; &lt;span style="color:#ae81ff">_&lt;/span>command.Execute();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>This practical guide has introduced several fundamental design patterns in C#, providing both conceptual understanding and practical implementations. Incorporating these patterns into your projects can significantly improve code organization, maintainability, and flexibility. As you delve deeper into C# development, consider exploring more design patterns tailored to your specific application needs.&lt;/p></description></item><item><title>Exploring What's New in C# 12</title><link>https://www.darrenhorrocks.co.uk/exploring-whats-new-c-12/</link><pubDate>Tue, 14 Nov 2023 21:15:20 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/exploring-whats-new-c-12/</guid><description>&lt;p>C# 12 has arrived, bringing a plethora of features aimed at enhancing developer productivity, simplifying code, and boosting application performance. Let&amp;rsquo;s delve into the key improvements introduced in this latest version.&lt;/p>
&lt;h2 id="getting-started-with-c-12">Getting Started with C# 12&lt;/h2>
&lt;p>To leverage the new features of C# 12, developers are encouraged to download .NET 8, the latest Visual Studio, or Visual Studio Code&amp;rsquo;s C# Dev Kit. Existing projects can be upgraded by indicating the desired language version, accomplished by changing the &lt;code>TargetFramework&lt;/code> to &lt;code>.NET 8.0&lt;/code> in the project file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>net8.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#75715e">&amp;lt;!-- ... --&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="simplifying-code-for-better-productivity">Simplifying Code for Better Productivity&lt;/h2>
&lt;p>C# 12 focuses on streamlining code, making it more expressive and efficient without sacrificing performance. The improvements can be explored in detail in the &amp;ldquo;What’s new in C# 12&amp;rdquo; article on MS Learn, which also provides links to updated documentation.&lt;/p>
&lt;h3 id="collection-expressions">Collection Expressions&lt;/h3>
&lt;p>One notable enhancement is the introduction of collection expressions, offering a unified syntax for creating collections. Before C# 12, different scenarios required varied syntax for initializing collections. Now, developers can use a single syntax for arrays, lists, spans, and more:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">int&lt;/span>[] x1 = [&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">4&lt;/span>];
&lt;span style="color:#66d9ef">int&lt;/span>[] x2 = [];
WriteByteArray([&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>]);
List&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; x4 = [&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">4&lt;/span>];
Span&amp;lt;DateTime&amp;gt; dates = [GetDate(&lt;span style="color:#ae81ff">0&lt;/span>), GetDate(&lt;span style="color:#ae81ff">1&lt;/span>)];
WriteByteSpan([&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>]);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The compiler generates optimized code, often improving performance and avoiding unnecessary data copying. Additionally, a new spread operator allows the inclusion of elements from multiple collections within a single expression.&lt;/p>
&lt;h3 id="primary-constructors-on-any-class-or-struct">Primary Constructors on Any Class or Struct&lt;/h3>
&lt;p>C# 12 extends primary constructors to work on all classes and structs, not just records. This feature enables the definition of constructor parameters directly in the class declaration, simplifying code and reducing boilerplate:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">BankAccount&lt;/span>(&lt;span style="color:#66d9ef">string&lt;/span> accountID, &lt;span style="color:#66d9ef">string&lt;/span> owner)
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> AccountID { &lt;span style="color:#66d9ef">get&lt;/span>; } = accountID;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Owner { &lt;span style="color:#66d9ef">get&lt;/span>; } = owner;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> ToString() =&amp;gt; &lt;span style="color:#e6db74">$&amp;#34;Account ID: {AccountID}, Owner: {Owner}&amp;#34;&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Primary constructors serve various purposes, such as providing arguments to a base constructor, initializing fields or properties, and reducing boilerplate in dependency injection.&lt;/p>
&lt;h3 id="alias-any-type">Alias Any Type&lt;/h3>
&lt;p>C# 12 introduces the ability to alias any type using &lt;code>using&lt;/code> alias directives. This allows developers to remove complex type signatures from their code, improving readability:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> intArray = &lt;span style="color:#66d9ef">int&lt;/span>[]; &lt;span style="color:#75715e">// Array types.
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">using&lt;/span> Point = (&lt;span style="color:#66d9ef">int&lt;/span> x, &lt;span style="color:#66d9ef">int&lt;/span> y); &lt;span style="color:#75715e">// Tuple type
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">using&lt;/span> unsafe ArrayPtr = &lt;span style="color:#66d9ef">int&lt;/span>*; &lt;span style="color:#75715e">// Pointer type (requires &amp;#34;unsafe&amp;#34;)
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These type aliases can be used at the top of a file and in global &lt;code>using&lt;/code> statements.&lt;/p>
&lt;h3 id="default-lambda-parameters">Default Lambda Parameters&lt;/h3>
&lt;p>Lambda expressions in C# 12 support default parameters, allowing developers to skip passing values and add parameters to existing lambda expressions without breaking calling code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> IncrementBy = (&lt;span style="color:#66d9ef">int&lt;/span> source, &lt;span style="color:#66d9ef">int&lt;/span> increment = &lt;span style="color:#ae81ff">1&lt;/span>) =&amp;gt; source + increment;
Console.WriteLine(IncrementBy(&lt;span style="color:#ae81ff">5&lt;/span>)); &lt;span style="color:#75715e">// 6
&lt;/span>&lt;span style="color:#75715e">&lt;/span>Console.WriteLine(IncrementBy(&lt;span style="color:#ae81ff">5&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>)); &lt;span style="color:#75715e">// 7
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This feature simplifies accessing lambda expressions and aligns with the simplicity offered by default parameters in regular methods.&lt;/p>
&lt;h2 id="making-code-faster-with-raw-memory-improvements">Making Code Faster with Raw Memory Improvements&lt;/h2>
&lt;p>C# 12 continues to enhance raw memory capabilities, contributing to improved application performance. Two key additions are &lt;code>ref readonly parameters&lt;/code> and &lt;code>inline arrays&lt;/code>.&lt;/p>
&lt;h3 id="ref-readonly-parameters">Ref Readonly Parameters&lt;/h3>
&lt;p>The introduction of &lt;code>ref readonly parameters&lt;/code> provides a final combination of passing parameters by reference or by value. These parameters must be variables, enhancing memory optimization:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">void&lt;/span> ProcessData(&lt;span style="color:#66d9ef">ref&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> data)
{
&lt;span style="color:#75715e">// Access data without modification
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="inline-arrays">Inline Arrays&lt;/h3>
&lt;p>Inline arrays offer a safe way to work with memory buffers, providing a struct-based, fixed-length array type. Developers can manipulate blocks of memory without enabling unsafe code, thereby improving application performance:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">InlineArray&lt;/span>&amp;lt;T&amp;gt;
{
&lt;span style="color:#66d9ef">private&lt;/span> T[] data;
&lt;span style="color:#75715e">// Implementation details...
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="experimental-features-pushing-the-boundaries">Experimental Features: Pushing the Boundaries&lt;/h2>
&lt;p>C# 12 introduces experimental features, including the &lt;code>experimental attribute&lt;/code> and &lt;code>interceptors&lt;/code>, aimed at gathering feedback and exploring new possibilities.&lt;/p>
&lt;h3 id="experimental-attribute">Experimental Attribute&lt;/h3>
&lt;p>The &lt;code>experimental attribute&lt;/code> is applied to types, members, and assemblies, signaling that the feature is under exploration and not yet committed to the implementation. This allows developers to suppress errors for individual experimental features and explore them using explicit compiler options or pragmas.&lt;/p>
&lt;h3 id="interceptors">Interceptors&lt;/h3>
&lt;p>Interceptors, available in preview mode with C# 12, enable the redirection of method calls. This experimental feature allows an optimized version of a method, generated for specific parameters, to replace a less efficient generalized method.&lt;/p>
&lt;p>Developers interested in these experimental features are encouraged to mark their code accordingly, using the &lt;code>ExperimentalAttribute&lt;/code> to indicate the experimental nature of their libraries.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>C# 12 introduces a wealth of features aimed at improving developer productivity, simplifying code, and enhancing application performance. From unified collection expressions to primary constructors on any class or struct, type aliases, default lambda parameters, and raw memory improvements, developers have an array of tools to streamline their code and build faster, more efficient applications. Additionally, the inclusion of experimental features demonstrates a commitment to exploring new possibilities and gathering valuable feedback from the developer community. As developers embrace C# 12, they can look forward to a more expressive and performant coding experience.&lt;/p></description></item><item><title>Optimizing SQL Queries in Entity Framework Core</title><link>https://www.darrenhorrocks.co.uk/optimizing-sql-queries-entity-framework-core/</link><pubDate>Sun, 12 Nov 2023 07:29:50 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/optimizing-sql-queries-entity-framework-core/</guid><description>&lt;p>Entity Framework Core (EF Core) is a powerful and widely used Object-Relational Mapping (ORM) framework for .NET applications. While EF Core simplifies database interactions, it&amp;rsquo;s essential to optimize SQL queries generated by EF Core to ensure efficient database performance. In this article, we&amp;rsquo;ll explore various strategies and techniques for optimizing SQL queries in Entity Framework Core, backed by examples and explanations.&lt;/p>
&lt;h2 id="lazy-loading-vs-eager-loading">&lt;strong>Lazy Loading vs. Eager Loading&lt;/strong>&lt;/h2>
&lt;p>EF Core supports lazy loading and eager loading for related entities. Lazy loading defers the loading of related entities until they are explicitly accessed, potentially leading to the N+1 query problem. Eager loading, on the other hand, loads related entities along with the main entity in a single query.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Lazy loading
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> order = dbContext.Orders.First();
&lt;span style="color:#66d9ef">var&lt;/span> customer = order.Customer; &lt;span style="color:#75715e">// Generates a separate query to load the customer
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">// Eager loading
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> orderWithCustomer = dbContext.Orders.Include(o =&amp;gt; o.Customer).First();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Explanation:&lt;/strong>
Use eager loading with the &lt;code>Include&lt;/code> method to fetch related entities in a single query, reducing the number of round trips to the database.&lt;/p>
&lt;h2 id="projection-for-specific-columns">&lt;strong>Projection for Specific Columns&lt;/strong>&lt;/h2>
&lt;p>Selecting only the necessary columns instead of retrieving all columns can significantly improve query performance.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Without projection
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> products = dbContext.Products.Where(p =&amp;gt; p.Category == &lt;span style="color:#e6db74">&amp;#34;Electronics&amp;#34;&lt;/span>).ToList();
&lt;span style="color:#75715e">// With projection
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> products = dbContext.Products
.Where(p =&amp;gt; p.Category == &lt;span style="color:#e6db74">&amp;#34;Electronics&amp;#34;&lt;/span>)
.Select(p =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { p.Id, p.Name, p.Price })
.ToList();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Explanation:&lt;/strong>
By projecting only the required columns, you reduce the amount of data retrieved from the database, leading to better performance.&lt;/p>
&lt;h2 id="batching-queries-with-asnotracking">&lt;strong>Batching Queries with &lt;code>AsNoTracking&lt;/code>&lt;/strong>&lt;/h2>
&lt;p>The &lt;code>AsNoTracking&lt;/code> method can be used to indicate that the entities retrieved from the database should not be tracked for changes. This can improve query performance, especially when dealing with read-only operations.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> orders = dbContext.Orders.AsNoTracking().ToList();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Explanation:&lt;/strong>
When entities are not tracked, EF Core doesn&amp;rsquo;t spend resources keeping track of changes, resulting in faster queries for read-only scenarios.&lt;/p>
&lt;h2 id="indexing-for-better-performance">&lt;strong>Indexing for Better Performance&lt;/strong>&lt;/h2>
&lt;p>Ensure that the database tables involved in EF Core queries are appropriately indexed. Indexing can significantly speed up data retrieval operations.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Index on the &amp;#39;Category&amp;#39; column
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">[Index(nameof(Category))]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Product&lt;/span>
{
&lt;span style="color:#75715e">// ... other properties
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Explanation:&lt;/strong>
Creating indexes on columns frequently used in queries improves database search performance.&lt;/p>
&lt;h2 id="optimizing-where-clauses">&lt;strong>Optimizing &lt;code>WHERE&lt;/code> Clauses&lt;/strong>&lt;/h2>
&lt;p>EF Core translates LINQ queries into SQL, but the generated SQL might not always be optimal. Be mindful of how complex LINQ expressions are translated into SQL queries, and consider using raw SQL for more control.&lt;/p>
&lt;p>&lt;strong>Example:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#75715e">// Inefficient WHERE clause
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> expensiveProducts = dbContext.Products.Where(p =&amp;gt; p.Price * &lt;span style="color:#ae81ff">1.2&lt;/span> &amp;gt; &lt;span style="color:#ae81ff">100&lt;/span>).ToList();
&lt;span style="color:#75715e">// Optimized WHERE clause
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> expensiveProducts = dbContext.Products.FromSqlRaw(&lt;span style="color:#e6db74">&amp;#34;SELECT * FROM Products WHERE Price &amp;gt; {0}&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">100&lt;/span> / &lt;span style="color:#ae81ff">1.2&lt;/span>).ToList();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Explanation:&lt;/strong>
In some cases, using raw SQL for complex queries can provide better performance than relying solely on LINQ.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Optimizing SQL queries in Entity Framework Core is crucial for ensuring the efficiency of database operations. By employing the strategies discussed in this article, you can enhance the performance of your EF Core queries and, in turn, deliver a more responsive and scalable application.&lt;/p></description></item><item><title>The Dual Nature of Artificial Intelligence: Strengths and Limitations</title><link>https://www.darrenhorrocks.co.uk/dual-nature-artificial-intelligence-strengths-limitations/</link><pubDate>Sat, 11 Nov 2023 07:21:53 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/dual-nature-artificial-intelligence-strengths-limitations/</guid><description>&lt;p>In the current landscape of technology, Artificial Intelligence stands out as &lt;strong>the&lt;/strong> transformative force with the potential to reshape industries and redefine how we interact with the world. While the capabilities of AI are undoubtedly impressive, a comprehensive understanding of its strengths and limitations is essential for harnessing its potential responsibly.&lt;/p>
&lt;h2 id="strengths-of-ai">Strengths of AI&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Data Processing and Pattern Recognition:&lt;/strong>
Artificial Intelligence excels in managing and interpreting vast datasets, far beyond the capacity of human capabilities. Machine learning algorithms enable AI systems to analyze complex patterns and trends swiftly. In the realm of healthcare, for instance, AI plays a pivotal role in diagnosing diseases by rapidly identifying subtle patterns in medical images, thereby enhancing accuracy and efficiency in patient care.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Repetitive and Rule-Based Tasks:&lt;/strong>
One of the hallmarks of AI is its ability to automate repetitive, rule-based tasks. In industries such as manufacturing and logistics, AI-powered robotic systems are deployed to perform monotonous duties with precision and consistency. This not only increases operational efficiency but also minimizes the risk of human errors, contributing to safer and more reliable processes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Natural Language Processing (NLP):&lt;/strong>
AI&amp;rsquo;s proficiency in Natural Language Processing (NLP) empowers it to understand, interpret, and generate human-like language. Virtual assistants, language translation services, and chatbots leverage NLP to facilitate seamless communication across various domains. These technologies play a vital role in enhancing user experiences, breaking down language barriers, and providing efficient solutions in customer service and information retrieval.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="limitations-of-ai">Limitations of AI&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Lack of Common Sense and Context Understanding:&lt;/strong>
Despite its prowess in data analysis, AI often struggles with understanding common sense and interpreting context as effortlessly as humans. In scenarios requiring nuanced interpretation or situations where background knowledge is crucial, AI systems may falter. This limitation poses challenges in areas such as customer service, content creation, and interpersonal communication, where context is paramount for accurate decision-making.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Ethical Decision-Making:&lt;/strong>
The ethical dimension of decision-making remains a significant challenge for AI. While AI systems can process data and make decisions based on predefined algorithms, they lack the inherent ethical compass and moral reasoning that humans possess. Issues related to bias in AI decision-making have raised concerns, particularly in critical areas such as criminal justice and hiring processes, emphasizing the need for human oversight to ensure fairness and accountability.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Creativity and Emotional Intelligence:&lt;/strong>
The creative and emotional realms remain distinctly human domains where AI faces limitations. While AI can generate content, mimic artistic styles, and compose music, it often lacks the depth of originality and emotional nuance that human creativity brings to these endeavors. Fields such as art, literature, and music thrive on the unique perspectives, emotions, and imaginative capacities of humans, underscoring the irreplaceable nature of the human touch in creative pursuits.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Artificial Intelligence represents a double-edged sword, offering unparalleled strengths in data processing, automation, and language understanding, yet grappling with challenges related to common sense, ethical decision-making, and the intricacies of creativity. As we navigate the evolving landscape of AI, a nuanced approach is crucial, emphasizing responsible deployment and ongoing collaboration between humans and machines. By leveraging AI&amp;rsquo;s strengths while addressing its limitations, we can pave the way for a harmonious integration of technology into our complex and dynamic world, ensuring that it serves humanity in the most beneficial and ethical manner possible.&lt;/p></description></item><item><title>From Chaos to Clarity: The Tale of PHP and .NET Naming</title><link>https://www.darrenhorrocks.co.uk/from-chaos-to-clarity-tale-php-net-naming/</link><pubDate>Fri, 10 Nov 2023 07:24:39 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/from-chaos-to-clarity-tale-php-net-naming/</guid><description>&lt;p>Programming languages play a crucial role in shaping the way developers write code. Among the myriad of languages, PHP and .NET stand out for their distinct approaches to naming conventions. In this article, we&amp;rsquo;ll explore the stark differences in naming conventions between PHP and .NET and the reasons behind them.&lt;/p>
&lt;h2 id="the-world-of-php-a-landscape-of-inconsistencies">&lt;strong>The World of PHP: A Landscape of Inconsistencies&lt;/strong>&lt;/h2>
&lt;p>PHP, a popular scripting language, has a reputation for its loose and sometimes inconsistent naming conventions. This can be attributed to its evolution and the diverse set of contributors over the years. Let&amp;rsquo;s delve into some of the key characteristics that define PHP naming:&lt;/p>
&lt;h3 id="1-case-sensitivity">1. &lt;strong>Case Sensitivity&lt;/strong>&lt;/h3>
&lt;p>One of the notable features of PHP is its case-insensitive nature. This means that variables, functions, and class names are not case-sensitive. For example, &lt;code>$myVariable&lt;/code>, &lt;code>$myvariable&lt;/code>, and &lt;code>$MyVariable&lt;/code> all refer to the same variable.&lt;/p>
&lt;h3 id="2-underscore-vs-camel-case">2. &lt;strong>Underscore vs. Camel Case&lt;/strong>&lt;/h3>
&lt;p>PHP exhibits a mix of naming styles. While underscores are commonly used to separate words in variable names (&lt;code>$my_variable&lt;/code>), camel case is also prevalent in function and method names (&lt;code>myFunction()&lt;/code>).&lt;/p>
&lt;h3 id="3-global-functions-and-namespace-collision">3. &lt;strong>Global Functions and Namespace Collision&lt;/strong>&lt;/h3>
&lt;p>PHP includes a vast set of global functions, which can sometimes lead to naming conflicts. This underscores the importance of properly namespacing functions and classes in larger projects.&lt;/p>
&lt;h2 id="the-net-paradigm-a-world-of-rigorous-naming-conventions">&lt;strong>The .NET Paradigm: A World of Rigorous Naming Conventions&lt;/strong>&lt;/h2>
&lt;p>In stark contrast to PHP, the .NET framework adheres to a set of strict and consistent naming conventions. These conventions are designed to promote readability, maintainability, and interoperability across different parts of the .NET ecosystem:&lt;/p>
&lt;h3 id="1-pascal-case-and-camel-case">1. &lt;strong>Pascal Case and Camel Case&lt;/strong>&lt;/h3>
&lt;p>In .NET, Pascal case is the standard for naming classes, methods, and properties (&lt;code>MyClass&lt;/code>, &lt;code>MyMethod&lt;/code>, &lt;code>MyProperty&lt;/code>). Camel case is used for naming parameters and local variables (&lt;code>myVariable&lt;/code>, &lt;code>myParameter&lt;/code>).&lt;/p>
&lt;h3 id="2-namespaces-for-clarity">2. &lt;strong>Namespaces for Clarity&lt;/strong>&lt;/h3>
&lt;p>.NET encourages the use of namespaces to organise and group related classes and prevent naming collisions. This helps maintain a clear and structured codebase, especially in larger projects.&lt;/p>
&lt;h3 id="3-consistency-across-the-framework">3. &lt;strong>Consistency Across the Framework&lt;/strong>&lt;/h3>
&lt;p>The .NET Framework, including languages like C# and VB.NET, maintains a high degree of naming consistency. This consistency extends to method names, event handlers, and design patterns, making it easier for developers to navigate and understand code.&lt;/p>
&lt;h2 id="reasons-behind-the-divide">&lt;strong>Reasons Behind the Divide&lt;/strong>&lt;/h2>
&lt;p>The differing naming conventions in PHP and .NET can be attributed to their respective histories, design philosophies, and communities.&lt;/p>
&lt;p>PHP&amp;rsquo;s evolution has been marked by a more relaxed and adaptable approach, allowing for quick development and prototyping. This flexibility can lead to a wide range of naming styles, reflecting the diverse backgrounds and preferences of PHP developers.&lt;/p>
&lt;p>On the other hand, the .NET framework was designed with a strong emphasis on enterprise-level development, where consistency and maintainability are paramount. The framework&amp;rsquo;s strict naming conventions facilitate collaboration and codebase longevity.&lt;/p>
&lt;h2 id="striking-a-balance">&lt;strong>Striking a Balance&lt;/strong>&lt;/h2>
&lt;p>While PHP and .NET have distinct naming conventions, it&amp;rsquo;s important to remember that both have their strengths and can be leveraged effectively in different contexts. PHP&amp;rsquo;s flexibility can be advantageous in rapid development scenarios, while .NET&amp;rsquo;s rigor provides a solid foundation for large-scale, long-term projects.&lt;/p>
&lt;p>In the end, the choice of naming conventions should align with the specific needs and goals of a project. Understanding the nuances of each language&amp;rsquo;s conventions empowers developers to make informed decisions that lead to cleaner, more maintainable code.&lt;/p>
&lt;hr>
&lt;p>Navigating the world of naming conventions is a crucial aspect of software development. Whether you&amp;rsquo;re in the dynamic realm of PHP or the structured environment of .NET, mastering the art of naming will undoubtedly contribute to code that is not only functional but also a pleasure to work with. Happy coding!&lt;/p></description></item><item><title>Why is AI So Bad at Financial Trading When Computers Are Just Better at Maths</title><link>https://www.darrenhorrocks.co.uk/why-ai-so-bad-at-financial-trading-when-computers-are-just-better-at-maths/</link><pubDate>Thu, 09 Nov 2023 07:16:20 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/why-ai-so-bad-at-financial-trading-when-computers-are-just-better-at-maths/</guid><description>&lt;p>One thing that everybody would have assumed that Machine Learning/AI would have been good at, given it is very good at forcasting, anomaly detection and maths in general, is financial trading. The problem is that several studies have shown that AI running on some very powerful machines is no better than any given human trader.&lt;/p>
&lt;p>It made me wonder, why is AI so bad at trading compared to what it should be?&lt;/p>
&lt;p>There are several reasons why AI may not always perform as well as humans in financial trading:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Lack of Contextual Understanding&lt;/strong>: AI models, including sophisticated ones like GPT-3 or GPT-4, lack a true understanding of context. They generate responses based on patterns they&amp;rsquo;ve learned from training data, but they don&amp;rsquo;t actually comprehend the content in the way humans do. This can be a significant disadvantage in understanding complex financial markets.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Unpredictable Market Conditions&lt;/strong>: Financial markets can be highly unpredictable, especially in the short term. Sudden events, news, or geopolitical shifts can significantly impact prices, and it&amp;rsquo;s challenging for AI models to adapt quickly to such changes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Limited Training Data&lt;/strong>: AI models rely on historical data to make predictions. If a model is trained on a limited dataset, it may not have seen a wide enough range of market conditions to make accurate predictions in all situations.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Overfitting and Generalization Issues&lt;/strong>: AI models can sometimes become overly specialised in the data they were trained on. This is known as overfitting. They may not generalise well to new, unseen data, which is crucial in the constantly changing landscape of financial markets.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Lack of Common Sense and Intuition&lt;/strong>: Humans have a deep understanding of the world and financial systems that is difficult to replicate in AI. This includes common sense, intuition, and an ability to interpret non-quantitative information (like news events, geopolitical factors, etc.) that may influence market movements.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Regulatory Constraints&lt;/strong>: Financial markets are highly regulated, and AI models may not always comply with all regulatory requirements. Compliance with legal and ethical standards is crucial in trading.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Risk Management and Decision-making under Uncertainty&lt;/strong>: Humans can exercise judgment and make decisions under uncertainty, considering factors that are not easily quantifiable. They can also incorporate risk management strategies based on their experience and intuition, which can be difficult for AI models to replicate.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Lack of Emotional Intelligence&lt;/strong>: While emotions can sometimes lead to irrational decisions, they can also provide valuable insights and gut instincts. AI models don&amp;rsquo;t have emotions or instincts in the same way humans do.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Market Manipulation and Game Theory&lt;/strong>: Humans are often engaged in strategic behavior and game theory when participating in financial markets. Understanding and anticipating these behaviors can be challenging for AI models.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>It&amp;rsquo;s worth noting that while AI can have advantages in certain aspects of trading (e.g., processing large amounts of data quickly), it&amp;rsquo;s not a one-size-fits-all solution. Some trading strategies may benefit more from human intuition and judgment, while others may benefit from algorithmic approaches. In practice, a combination of human expertise and AI tools is often the most effective approach in financial trading.&lt;/p></description></item><item><title>Solving Common Database Performance Bottlenecks</title><link>https://www.darrenhorrocks.co.uk/solving-common-database-performance-bottlenecks/</link><pubDate>Wed, 08 Nov 2023 07:11:49 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/solving-common-database-performance-bottlenecks/</guid><description>&lt;p>A well-optimised database is the cornerstone of any high-performance application. However, as data grows and user demands increase, databases can face performance bottlenecks that hinder application responsiveness. In this article, we&amp;rsquo;ll explore some common database performance issues and strategies to overcome them.&lt;/p>
&lt;h2 id="1-inefficient-queries">1. &lt;strong>Inefficient Queries&lt;/strong>&lt;/h2>
&lt;p>Inefficient queries are a prevalent cause of database performance issues. These can lead to slow response times and put unnecessary strain on the database server. For example, a query that retrieves all columns when only a few are needed can be highly inefficient.&lt;/p>
&lt;p>&lt;strong>Solution: Query Optimization&lt;/strong>&lt;/p>
&lt;p>Consider the following example. Instead of using:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> users &lt;span style="color:#66d9ef">WHERE&lt;/span> status &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;active&amp;#39;&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Use:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> id, username, email &lt;span style="color:#66d9ef">FROM&lt;/span> users &lt;span style="color:#66d9ef">WHERE&lt;/span> status &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;active&amp;#39;&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This way, only the necessary columns are retrieved, reducing the workload on the database.&lt;/p>
&lt;h2 id="2-insufficient-indexing">2. &lt;strong>Insufficient Indexing&lt;/strong>&lt;/h2>
&lt;p>Insufficient or improper indexing can lead to full table scans, where the database has to search through every record to find the desired information. This can drastically slow down query performance.&lt;/p>
&lt;p>&lt;strong>Solution: Proper Indexing&lt;/strong>&lt;/p>
&lt;p>For example, suppose you have a table named &lt;code>products&lt;/code> with columns &lt;code>product_id&lt;/code> and &lt;code>category&lt;/code>. Creating an index on &lt;code>category&lt;/code> can significantly speed up queries like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> products &lt;span style="color:#66d9ef">WHERE&lt;/span> category &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;Electronics&amp;#39;&lt;/span>;
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="3-hardware-limitations">3. &lt;strong>Hardware Limitations&lt;/strong>&lt;/h2>
&lt;p>Hardware limitations can be a significant bottleneck for database performance. This includes limitations on CPU, memory, or disk I/O. As data and user load grow, the existing hardware may become insufficient.&lt;/p>
&lt;p>&lt;strong>Solution: Scaling and Optimization&lt;/strong>&lt;/p>
&lt;p>Vertical scaling involves upgrading individual components, like increasing CPU cores or adding more RAM. For instance, upgrading from a dual-core to a quad-core CPU can lead to substantial performance improvements.&lt;/p>
&lt;p>Horizontal scaling involves distributing the load across multiple servers. Techniques like database sharding, where data is partitioned across multiple servers, can significantly improve performance for large-scale applications.&lt;/p>
&lt;h2 id="4-inadequate-database-design">4. &lt;strong>Inadequate Database Design&lt;/strong>&lt;/h2>
&lt;p>Inadequate database design can lead to performance bottlenecks. This may include issues like redundant data, inefficient normalization, or poor use of data types.&lt;/p>
&lt;p>&lt;strong>Solution: Database Design Best Practices&lt;/strong>&lt;/p>
&lt;p>For instance, consider an e-commerce application. Properly normalizing tables for orders and products, while denormalizing for high-frequency queries like product listings, can strike a balance between performance and data integrity.&lt;/p>
&lt;h2 id="5-locking-and-concurrency-issues">5. &lt;strong>Locking and Concurrency Issues&lt;/strong>&lt;/h2>
&lt;p>Concurrency issues arise when multiple users or processes try to access and modify the same data simultaneously. This contention can lead to reduced performance and even deadlocks.&lt;/p>
&lt;p>&lt;strong>Solution: Optimistic Concurrency Control&lt;/strong>&lt;/p>
&lt;p>Optimistic Concurrency Control (OCC) is a technique that allows multiple transactions to proceed concurrently, assuming they won&amp;rsquo;t interfere with each other. It involves using timestamps or version numbers to track changes.&lt;/p>
&lt;h2 id="6-inadequate-database-maintenance">6. &lt;strong>Inadequate Database Maintenance&lt;/strong>&lt;/h2>
&lt;p>Regular maintenance tasks like vacuuming, reindexing, and cleaning up stale data are crucial for optimal database performance.&lt;/p>
&lt;p>&lt;strong>Solution: Scheduled Maintenance&lt;/strong>&lt;/p>
&lt;p>For example, setting up a nightly job to perform a VACUUM operation on a PostgreSQL database can reclaim space and improve performance. Additionally, regular index maintenance and statistics updates are essential for query optimization.&lt;/p>
&lt;h2 id="7-suboptimal-configuration">7. &lt;strong>Suboptimal Configuration&lt;/strong>&lt;/h2>
&lt;p>Database server configurations may not be optimised for the workload, leading to performance bottlenecks.&lt;/p>
&lt;p>&lt;strong>Solution: Configuration Tuning&lt;/strong>&lt;/p>
&lt;p>Regularly reviewing and adjusting configuration settings based on the specific workload and hardware specifications is crucial. For example, adjusting the maximum connection pool size or buffer cache size based on application demands can significantly improve performance.&lt;/p>
&lt;p>By identifying and addressing these common database performance bottlenecks, you can ensure your application runs smoothly, even under heavy loads. Remember that ongoing monitoring, testing, and optimization are key to maintaining high-performance databases in the long run.&lt;/p>
&lt;p>Happy optimizing!&lt;/p></description></item><item><title>Mastering Git: Tips and Tricks for Version Control</title><link>https://www.darrenhorrocks.co.uk/mastering-git-tips-tricks-version-control/</link><pubDate>Tue, 07 Nov 2023 07:01:41 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/mastering-git-tips-tricks-version-control/</guid><description>&lt;p>Version control is a crucial aspect of modern software development. It allows multiple developers to collaborate on a project while keeping track of changes and managing code efficiently. Among the various version control systems available, Git has emerged as one of the most popular and powerful tools.&lt;/p>
&lt;p>In this article, we&amp;rsquo;ll delve into some advanced Git tips and tricks, accompanied by practical examples, to help you become a more proficient version control practitioner.&lt;/p>
&lt;h2 id="1-branching-strategies">1. &lt;strong>Branching Strategies&lt;/strong>&lt;/h2>
&lt;p>Git&amp;rsquo;s branching capabilities are powerful. Adopting a solid branching strategy can significantly improve your workflow. Consider using strategies like Gitflow, GitHub Flow, or Trunk-Based Development, depending on the nature and size of your projects.&lt;/p>
&lt;p>&lt;strong>Example: Using Gitflow&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Initialise a new Gitflow repository&lt;/span>
git flow init
&lt;span style="color:#75715e"># Start a new feature branch&lt;/span>
git flow feature start new-feature
&lt;span style="color:#75715e"># Finish a feature branch&lt;/span>
git flow feature finish new-feature
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="2-rebasing-vs-merging">2. &lt;strong>Rebasing vs. Merging&lt;/strong>&lt;/h2>
&lt;p>Understanding when to use rebasing versus merging is key to maintaining a clean and readable Git history. While merging combines the changes from one branch into another, rebasing integrates changes by moving or combining a sequence of commits to a new base commit.&lt;/p>
&lt;p>&lt;strong>Example: Rebasing&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Switch to the target branch&lt;/span>
git checkout target-branch
&lt;span style="color:#75715e"># Perform a rebase&lt;/span>
git rebase source-branch
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="3-interactive-rebase">3. &lt;strong>Interactive Rebase&lt;/strong>&lt;/h2>
&lt;p>The interactive rebase allows you to edit, combine, or reorder commits before merging them into a branch. This can help maintain a cleaner commit history and resolve conflicts more effectively.&lt;/p>
&lt;p>&lt;strong>Example: Interactive Rebase&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Start an interactive rebase&lt;/span>
git rebase -i HEAD~3
&lt;span style="color:#75715e"># In the interactive editor, you can squash, reword, or reorder commits&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="4-stash-uncommitted-changes">4. &lt;strong>Stash Uncommitted Changes&lt;/strong>&lt;/h2>
&lt;p>The &lt;code>git stash&lt;/code> command is a lifesaver when you need to switch branches but have uncommitted changes. It temporarily saves your work and allows you to switch to a different branch without committing incomplete code.&lt;/p>
&lt;p>&lt;strong>Example: Stashing Changes&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Save current changes to the stash&lt;/span>
git stash
&lt;span style="color:#75715e"># Switch branches&lt;/span>
git checkout other-branch
&lt;span style="color:#75715e"># Apply stashed changes&lt;/span>
git stash apply
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="5-cherry-picking-commits">5. &lt;strong>Cherry-Picking Commits&lt;/strong>&lt;/h2>
&lt;p>Cherry-picking is a technique used to apply specific commits from one branch to another. This can be helpful when you want to selectively include changes without merging an entire branch.&lt;/p>
&lt;p>&lt;strong>Example: Cherry-Picking&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Switch to the target branch&lt;/span>
git checkout target-branch
&lt;span style="color:#75715e"># Cherry-pick a specific commit&lt;/span>
git cherry-pick &amp;lt;commit-hash&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="6-aliases">6. &lt;strong>Aliases&lt;/strong>&lt;/h2>
&lt;p>Git aliases allow you to create shortcuts for frequently used commands. For example, you can create an alias to replace &lt;code>git status&lt;/code> with a shorter command like &lt;code>git st&lt;/code>.&lt;/p>
&lt;p>&lt;strong>Example: Creating an Alias&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">git config --global alias.st status
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="7-custom-hooks">7. &lt;strong>Custom Hooks&lt;/strong>&lt;/h2>
&lt;p>Git hooks are scripts that Git executes before or after events like commit, push, and receive. By using custom hooks, you can automate tasks like code formatting, linting, or running tests.&lt;/p>
&lt;p>&lt;strong>Example: Pre-commit Hook&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Create a pre-commit hook script&lt;/span>
echo &lt;span style="color:#e6db74">&amp;#34;npm run lint&amp;#34;&lt;/span> &amp;gt; .git/hooks/pre-commit
&lt;span style="color:#75715e"># Make the script executable&lt;/span>
chmod +x .git/hooks/pre-commit
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="8-reflog">8. &lt;strong>Reflog&lt;/strong>&lt;/h2>
&lt;p>The reflog is a powerful tool for recovering lost commits or branches. It records the history of every HEAD change in the repository, allowing you to undo actions or find lost work.&lt;/p>
&lt;p>&lt;strong>Example: Viewing the Reflog&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># View the reflog&lt;/span>
git reflog
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="9-git-bisect">9. &lt;strong>Git Bisect&lt;/strong>&lt;/h2>
&lt;p>Git bisect is a powerful debugging tool that helps you find the specific commit where a bug was introduced. It uses a binary search algorithm to efficiently narrow down the problematic commit.&lt;/p>
&lt;p>&lt;strong>Example: Using Git Bisect&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Start the bisect process&lt;/span>
git bisect start
&lt;span style="color:#75715e"># Mark the current commit as bad&lt;/span>
git bisect bad
&lt;span style="color:#75715e"># Mark a known good commit&lt;/span>
git bisect good &amp;lt;commit-hash&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="10-using-git-worktrees">10. &lt;strong>Using Git Worktrees&lt;/strong>&lt;/h2>
&lt;p>Git worktrees allow you to have multiple working directories from a single repository. This can be incredibly useful when you need to work on different features concurrently.&lt;/p>
&lt;p>&lt;strong>Example: Creating a Worktree&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">&lt;span style="color:#75715e"># Create a new worktree&lt;/span>
git worktree add ../my-feature-branch feature-branch
&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>Mastering Git is a journey that requires practice and a willingness to explore its powerful features. By incorporating these tips and tricks into your workflow, you&amp;rsquo;ll not only become a more proficient Git user but also enhance your overall efficiency in version control.&lt;/p>
&lt;p>Remember, the key to success in version control is consistent practice and a willingness to learn from your experiences. Happy coding!&lt;/p></description></item><item><title>Image Recognition with C# and AI: A Practical Guide</title><link>https://www.darrenhorrocks.co.uk/image-recognition-with-c-ai-practical-guide/</link><pubDate>Mon, 06 Nov 2023 07:01:41 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/image-recognition-with-c-ai-practical-guide/</guid><description>&lt;p>Image recognition has become a fundamental component of many applications, from medical imaging, autonomous vehicles and even the ANPR cameras that make parking your car so much easier. In this article, we&amp;rsquo;ll explore how to implement image recognition using C# and harness the power of artificial intelligence (AI) to identify objects, scenes, and patterns within images.&lt;/p>
&lt;h2 id="understanding-image-recognition">Understanding Image Recognition&lt;/h2>
&lt;p>Image recognition, also known as computer vision, is the process of using software to analyze and interpret visual data from the world. This technology enables computers to &amp;ldquo;see&amp;rdquo; and understand the content of images, making it invaluable in a wide range of applications.&lt;/p>
&lt;h2 id="choosing-the-right-ai-frameworks">Choosing the Right AI Frameworks&lt;/h2>
&lt;p>When it comes to image recognition with C#, there are several powerful AI frameworks to consider. One popular choice is the TensorFlow.NET library, which provides a seamless interface to the TensorFlow deep learning framework within C#. Another option is Microsoft&amp;rsquo;s Cognitive Services, which offers pre-trained models for image recognition tasks.&lt;/p>
&lt;h2 id="getting-started-with-tensorflownet">Getting Started with TensorFlow.NET&lt;/h2>
&lt;p>TensorFlow.NET is an open-source library that allows you to utilise the TensorFlow framework in your C# applications. Let&amp;rsquo;s walk through a simple example of image recognition using TensorFlow.NET:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> TensorFlow;
&lt;span style="color:#66d9ef">using&lt;/span> System;
&lt;span style="color:#66d9ef">using&lt;/span> System.Drawing;
&lt;span style="color:#66d9ef">using&lt;/span> System.IO;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">var&lt;/span> modelPath = &lt;span style="color:#e6db74">&amp;#34;&amp;lt;PATH_TO_YOUR_MODEL&amp;gt;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> imageFilePath = &lt;span style="color:#e6db74">&amp;#34;&amp;lt;PATH_TO_YOUR_IMAGE&amp;gt;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> session = &lt;span style="color:#66d9ef">new&lt;/span> TFSession())
{
&lt;span style="color:#66d9ef">var&lt;/span> graph = &lt;span style="color:#66d9ef">new&lt;/span> TFGraph();
&lt;span style="color:#66d9ef">var&lt;/span> model = File.ReadAllBytes(modelPath);
graph.Import(model);
&lt;span style="color:#66d9ef">var&lt;/span> tensor = ImageUtil.CreateTensorFromImageFile(imageFilePath);
&lt;span style="color:#66d9ef">var&lt;/span> runner = session.GetRunner();
runner.AddInput(graph[&lt;span style="color:#e6db74">&amp;#34;input&amp;#34;&lt;/span>][&lt;span style="color:#ae81ff">0&lt;/span>], tensor).Fetch(graph[&lt;span style="color:#e6db74">&amp;#34;output&amp;#34;&lt;/span>][&lt;span style="color:#ae81ff">0&lt;/span>]);
&lt;span style="color:#66d9ef">var&lt;/span> output = runner.Run();
&lt;span style="color:#66d9ef">var&lt;/span> result = output[&lt;span style="color:#ae81ff">0&lt;/span>].GetValue() &lt;span style="color:#66d9ef">as&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span>[,];
&lt;span style="color:#75715e">// Process the result for interpretation
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// ...
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we load a pre-trained model into TensorFlow.NET and use it to perform image recognition on an input image file.&lt;/p>
&lt;h2 id="utilizing-microsoft-cognitive-services">Utilizing Microsoft Cognitive Services&lt;/h2>
&lt;p>Microsoft Cognitive Services provides a range of pre-trained models for image recognition tasks. Let&amp;rsquo;s look at how to use the Computer Vision API for image analysis:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.Azure.CognitiveServices.Vision.ComputerVision;
&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
&lt;span style="color:#66d9ef">using&lt;/span> System;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">var&lt;/span> endpoint = &lt;span style="color:#e6db74">&amp;#34;&amp;lt;YOUR_COGNITIVE_SERVICES_ENDPOINT&amp;gt;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> apiKey = &lt;span style="color:#e6db74">&amp;#34;&amp;lt;YOUR_API_KEY&amp;gt;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> client = &lt;span style="color:#66d9ef">new&lt;/span> ComputerVisionClient(&lt;span style="color:#66d9ef">new&lt;/span> ApiKeyServiceClientCredentials(apiKey))
{
Endpoint = endpoint
};
&lt;span style="color:#66d9ef">var&lt;/span> imageFilePath = &lt;span style="color:#e6db74">&amp;#34;&amp;lt;PATH_TO_YOUR_IMAGE&amp;gt;&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> features = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;VisualFeatureTypes&amp;gt;
{
VisualFeatureTypes.Description,
VisualFeatureTypes.Categories,
VisualFeatureTypes.Tags
};
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> stream = File.OpenRead(imageFilePath))
{
&lt;span style="color:#66d9ef">var&lt;/span> analysis = &lt;span style="color:#66d9ef">await&lt;/span> client.AnalyzeImageInStreamAsync(stream, features);
&lt;span style="color:#75715e">// Process the analysis results
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// ...
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we use the Computer Vision API to analyze an image and obtain descriptive information about its content.&lt;/p>
&lt;h2 id="real-world-applications">Real-world Applications&lt;/h2>
&lt;p>Image recognition has found applications in various fields, including healthcare, autonomous vehicles, retail, and more. Whether it&amp;rsquo;s identifying diseases in medical images or enabling self-driving cars to navigate safely, the possibilities are vast.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>By combining the power of C# with AI, you can implement image recognition in your applications, opening up a world of possibilities for visual data analysis. Whether you choose TensorFlow.NET or leverage Microsoft&amp;rsquo;s Cognitive Services, the ability to interpret images can revolutionise the capabilities of your software. So, dive in, start experimenting, and unlock the potential of image recognition in your projects!&lt;/p></description></item><item><title>Unleashing the Power of C#: Integrating AI for Intelligent Applications</title><link>https://www.darrenhorrocks.co.uk/power-ai-c-applications/</link><pubDate>Sun, 05 Nov 2023 18:00:00 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/power-ai-c-applications/</guid><description>&lt;p>Harnessing the potential of artificial intelligence (AI) can elevate your C# applications to a new level of intelligence. In this article, we&amp;rsquo;ll explore how to seamlessly integrate AI into your C# projects, with practical examples to illustrate each step.&lt;/p>
&lt;p>C# is a versatile and powerful programming language that excels in creating robust applications across various domains. By incorporating AI capabilities, you can empower your applications to not only process data but also learn from it, enabling them to make informed decisions.&lt;/p>
&lt;h3 id="choosing-the-right-ai-libraries-for-c">Choosing the Right AI Libraries for C#&lt;/h3>
&lt;p>Before diving into development, it&amp;rsquo;s crucial to select the appropriate AI libraries and tools for your C# project. A popular choice is ML.NET, an open-source machine learning framework developed by Microsoft. Let&amp;rsquo;s take a look at a simple example of how to use ML.NET for sentiment analysis:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.ML;
&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.ML.Data;
&lt;span style="color:#66d9ef">using&lt;/span> System;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SentimentData&lt;/span>
{
&lt;span style="color:#a6e22e"> [LoadColumn(0)]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> SentimentText;
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [LoadColumn(1), ColumnName(&amp;#34;Label&amp;#34;)]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> Sentiment;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SentimentPrediction&lt;/span> : SentimentData
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> Score;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> Probability;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> PredictedLabel;
}
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> MLContext();
&lt;span style="color:#75715e">// Load data
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> data = context.Data.LoadFromTextFile&amp;lt;SentimentData&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;sentiment_data.csv&amp;#34;&lt;/span>, separatorChar: &lt;span style="color:#e6db74">&amp;#39;,&amp;#39;&lt;/span>);
&lt;span style="color:#75715e">// Define pipeline
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> pipeline = context.Transforms.Text.FeaturizeText(&lt;span style="color:#e6db74">&amp;#34;Features&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;SentimentText&amp;#34;&lt;/span>)
.Append(context.Transforms.CopyColumns(&lt;span style="color:#e6db74">&amp;#34;Label&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;Sentiment&amp;#34;&lt;/span>))
.Append(context.Transforms.NormalizeMinMax(&lt;span style="color:#e6db74">&amp;#34;Features&amp;#34;&lt;/span>))
.Append(context.Transforms.Conversion.MapValueToKey(&lt;span style="color:#e6db74">&amp;#34;Label&amp;#34;&lt;/span>))
.Append(context.Transforms.Concatenate(&lt;span style="color:#e6db74">&amp;#34;Features&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;Features&amp;#34;&lt;/span>))
.Append(context.Transforms.NormalizeMinMax(&lt;span style="color:#e6db74">&amp;#34;Features&amp;#34;&lt;/span>))
.Append(context.Model.LoadTensorFlowModel(&lt;span style="color:#e6db74">&amp;#34;model.pb&amp;#34;&lt;/span>)
.ScoreTensorName(&lt;span style="color:#e6db74">&amp;#34;dense_2/Softmax&amp;#34;&lt;/span>)
.AddInput(&lt;span style="color:#e6db74">&amp;#34;conv1d_input&amp;#34;&lt;/span>, name =&amp;gt; name == &lt;span style="color:#e6db74">&amp;#34;conv1d_input&amp;#34;&lt;/span>)
.AddOutput(&lt;span style="color:#e6db74">&amp;#34;dense_2/Softmax&amp;#34;&lt;/span>)
)
.Append(context.Transforms.CopyColumns(&lt;span style="color:#e6db74">&amp;#34;Score&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;PredictedLabel&amp;#34;&lt;/span>));
&lt;span style="color:#75715e">// Train model
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> model = pipeline.Fit(data);
&lt;span style="color:#75715e">// Make predictions
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> predictionEngine = context.Model.CreatePredictionEngine&amp;lt;SentimentData, SentimentPrediction&amp;gt;(model);
&lt;span style="color:#66d9ef">var&lt;/span> sentimentData = &lt;span style="color:#66d9ef">new&lt;/span> SentimentData { SentimentText = &lt;span style="color:#e6db74">&amp;#34;This is great!&amp;#34;&lt;/span> };
&lt;span style="color:#66d9ef">var&lt;/span> prediction = predictionEngine.Predict(sentimentData);
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Predicted Sentiment: {(prediction.Sentiment ? &amp;#34;&lt;/span>Positive&lt;span style="color:#e6db74">&amp;#34; : &amp;#34;&lt;/span>Negative&lt;span style="color:#e6db74">&amp;#34;)}&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we use ML.NET to perform sentiment analysis on text data. The model is trained to predict whether a given text conveys a positive or negative sentiment.&lt;/p>
&lt;h3 id="exploring-nlp-capabilities-in-c">Exploring NLP Capabilities in C#&lt;/h3>
&lt;p>Natural Language Processing (NLP) is a crucial component of many AI applications. With C#, you can leverage libraries like OpenNLP or integrate with Azure Cognitive Services for NLP tasks. Let&amp;rsquo;s look at a simple example of sentiment analysis using Azure Cognitive Services:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.Azure.CognitiveServices.Language.TextAnalytics;
&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.Azure.CognitiveServices.Language.TextAnalytics.Models;
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">var&lt;/span> client = &lt;span style="color:#66d9ef">new&lt;/span> TextAnalyticsClient(&lt;span style="color:#66d9ef">new&lt;/span> ApiKeyServiceClientCredentials(&lt;span style="color:#e6db74">&amp;#34;&amp;lt;YOUR_API_KEY&amp;gt;&amp;#34;&lt;/span>))
{
Endpoint = &lt;span style="color:#e6db74">&amp;#34;&amp;lt;YOUR_ENDPOINT&amp;gt;&amp;#34;&lt;/span>
};
&lt;span style="color:#66d9ef">var&lt;/span> sentiment = client.Sentiment(&lt;span style="color:#e6db74">&amp;#34;&amp;lt;YOUR_LANGUAGE&amp;gt;&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">new&lt;/span> MultiLanguageInput
{
Id = &lt;span style="color:#e6db74">&amp;#34;1&amp;#34;&lt;/span>,
Text = &lt;span style="color:#e6db74">&amp;#34;This is great!&amp;#34;&lt;/span>
});
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;Sentiment Score: {sentiment.Score}&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we utilise Azure Cognitive Services for sentiment analysis. The service provides a sentiment score, indicating the positivity or negativity of the provided text.&lt;/p>
&lt;h3 id="real-world-applications-and-beyond">Real-world Applications and Beyond&lt;/h3>
&lt;p>These examples only scratch the surface of what&amp;rsquo;s possible when combining C# with AI. From recommendation systems to image recognition, the integration of AI expands the horizons of what your C# applications can achieve.&lt;/p>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>By integrating AI into your C# applications, you can create intelligent, data-driven solutions that excel in tasks ranging from sentiment analysis to complex machine learning models. With the right tools and techniques, your C# projects can leverage the power of AI to provide enhanced functionality and decision-making capabilities.&lt;/p></description></item><item><title>Setting Up Bitlocker With SSD/NVME Hardware Encryption (it can be faster than no encryption at all)</title><link>https://www.darrenhorrocks.co.uk/setting-up-bitlocker-with-ssd-hardware-encryption/</link><pubDate>Tue, 24 Oct 2023 11:23:23 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/setting-up-bitlocker-with-ssd-hardware-encryption/</guid><description>&lt;p>&lt;a href="https://www.neowin.net/news/microsofts-default-bitlocker-on-your-windows-11-pc-is-hitting-even-the-fastest-ssds-hard/">It has recently been reported&lt;/a> that Windows 11&amp;rsquo;s default always-on bitlocker protection, with software encryption, &lt;a href="https://www.tomshardware.com/news/windows-software-bitlocker-slows-performance">could be crippling upto 45% of your storage performance&lt;/a>.&lt;/p>
&lt;p>To use Bitlocker with SSD Hardware full disk encryption, it&amp;rsquo;s crucial that your SSD supports TCG Opal and the eDrive standard (IEEE-1667). Unfortunately, many manufacturers omit this information from their datasheets, making it hard to find. To identify compatible devices with TCG Opal support, you can use the filter on the German Website &lt;a href="https://geizhals.de/?cat=hdssd&amp;amp;xf=8525_TCG+Opal">Geizhals&lt;/a>.&lt;/p>
&lt;p>You will also require the manufacturer&amp;rsquo;s software for your SSD, such as Samsung Magician or Kingston’s SSD Manager.&lt;/p>
&lt;h2 id="where-to-start">Where to start&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>SSD Compatibility Check:&lt;/p>
&lt;ul>
&lt;li>Ensure your SSD supports TCG Opal and eDrive standard.&lt;/li>
&lt;li>Visit &lt;a href="https://geizhals.de/?cat=hdssd&amp;amp;xf=8525_TCG+Opal">Geizhals&lt;/a> for a filtered list of compatible devices.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Obtain Manufacturer&amp;rsquo;s Software:&lt;/p>
&lt;ul>
&lt;li>Download and install the specific software for your SSD (e.g., Samsung Magician or Kingston’s SSD Manager).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Windows 2 Go Installation (Optional but Recommended):&lt;/p>
&lt;ul>
&lt;li>Download the latest Windows ISO from &lt;a href="https://www.microsoft.com/software-download/windows11">Microsoft&lt;/a>.&lt;/li>
&lt;li>Create a Windows 2 Go installation on a fast USB Stick, preferably NVME based, using &lt;a href="https://rufus.ie">Rufus&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="what-to-do-next">What to do next&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>BIOS Configuration&lt;/p>
&lt;ul>
&lt;li>Install the SSD into the target computer.&lt;/li>
&lt;li>Disable the Compatibility Support Module (CSM) in the BIOS. This option is typically found in the BOOT settings, though it may be hidden if fast boot is enabled.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Boot into Windows Installation&lt;/p>
&lt;ul>
&lt;li>Boot into a Windows installation; ideally, use a USB Stick with Windows 2 Go.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>SSD Software Component Installation:&lt;/p>
&lt;ul>
&lt;li>Install the SSD software component and locate the option to prepare the drive for encryption. In Samsung Magician, find it under &amp;ldquo;Data Management - Encrypted Drive,&amp;rdquo; while in Kingston’s SSD Manager, it&amp;rsquo;s called &amp;ldquo;IEEE 1667 Enable.&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Secure Erase:&lt;/p>
&lt;ul>
&lt;li>Perform a secure erase of the drive. You can do this directly in the tool or through BIOS options (found in the &amp;ldquo;Tools&amp;rdquo; section). Alternatively, you can use hdparm on Linux or diskpart on Windows.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Block SID Configuration:&lt;/p>
&lt;ul>
&lt;li>Disable &amp;ldquo;Block SID&amp;rdquo; in the BIOS alongside TPM configuration. If this option is not available in the BIOS, follow these steps in a Windows 2 Go installation:
&lt;ul>
&lt;li>Open Powershell as administrator.&lt;/li>
&lt;li>Run: &lt;code>$tpm = gwmi -n root\cimv2\security\microsofttpm win32_tpm&lt;/code>&lt;/li>
&lt;li>Run: &lt;code>$tpm.SetPhysicalPresenceRequest(97)&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Reboot.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Complete Windows Installation:&lt;/p>
&lt;ul>
&lt;li>On the next boot, follow the prompts on the POST screen.&lt;/li>
&lt;li>Plug in your Windows installation stick and remove the Windows 2 Go stick.&lt;/li>
&lt;li>Press &amp;ldquo;F10&amp;rdquo; to accept the command, and the computer will reboot.&lt;/li>
&lt;li>Install Windows on the target SSD without accessing BIOS or Boot menu beforehand.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Enable Hardware-Based Encryption via Group Policy&lt;/p>
&lt;ul>
&lt;li>Open &amp;ldquo;group policies&amp;rdquo; via start.&lt;/li>
&lt;li>Navigate to Computer Configuration &amp;gt; Administrative Templates &amp;gt; Windows Components &amp;gt; Bitlocker &amp;gt; System Drive &amp;gt; Administrative Configure use of hardware-based encryption for fixed data drives.&lt;/li>
&lt;li>Activate the policy and disable fallback to software encryption in the lower left.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Encrypt System Drive:&lt;/p>
&lt;ul>
&lt;li>Open Bitlocker and initiate the encryption process for your system drive.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Optional Step (if &amp;ldquo;Block SID&amp;rdquo; config is persistent):&lt;/p>
&lt;ul>
&lt;li>If the POST screen indicates that the &amp;ldquo;Block SID&amp;rdquo; configuration is persistent, re-enable it either in the BIOS or via Windows Powershell:
&lt;ul>
&lt;li>Open Powershell as admin in Windows 2 Go.&lt;/li>
&lt;li>Run: &lt;code>$tpm = gwmi -n root\cimv2\security\microsofttpm win32_tpm&lt;/code>&lt;/li>
&lt;li>Run: &lt;code>$tpm.SetPhysicalPresenceRequest(96)&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Reboot.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Enjoy better SSD performance&amp;hellip; 😁&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Best Practices for Exception Handling in C#</title><link>https://www.darrenhorrocks.co.uk/best-practices-exception-handling-c-sharp/</link><pubDate>Wed, 18 Oct 2023 08:35:32 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/best-practices-exception-handling-c-sharp/</guid><description>&lt;p>Exception handling is a critical aspect of robust software development. Properly managing exceptions ensures that your application can gracefully handle errors and recover from unexpected situations. In this article, we&amp;rsquo;ll explore the best practices for effective exception handling in C#.&lt;/p>
&lt;h2 id="use-specific-exception-types">Use Specific Exception Types&lt;/h2>
&lt;p>When catching exceptions, be specific about the type of exception you&amp;rsquo;re handling. This allows you to differentiate between different error scenarios and take appropriate actions.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#75715e">// Code that may throw an exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (ArgumentNullException ex)
{
&lt;span style="color:#75715e">// Handle ArgumentNullException
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (InvalidOperationException ex)
{
&lt;span style="color:#75715e">// Handle InvalidOperationException
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="avoid-catch-all-blocks">Avoid Catch-All Blocks&lt;/h2>
&lt;p>While it may be tempting to use a catch-all block (catching &lt;code>Exception&lt;/code>), it&amp;rsquo;s generally better to handle specific exceptions. Catching all exceptions can mask unexpected errors and make debugging more challenging.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#75715e">// Code that may throw an exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (Exception ex)
{
&lt;span style="color:#75715e">// Avoid using a catch-all block unless absolutely necessary
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="use-finally-blocks-for-cleanup">Use Finally Blocks for Cleanup&lt;/h2>
&lt;p>The &lt;code>finally&lt;/code> block is executed regardless of whether an exception is thrown. It&amp;rsquo;s ideal for releasing resources or performing cleanup operations.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#75715e">// Code that may throw an exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (Exception ex)
{
&lt;span style="color:#75715e">// Handle the exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">finally&lt;/span>
{
&lt;span style="color:#75715e">// Cleanup code (e.g., closing files or connections)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="throw-exceptions-with-meaningful-messages">Throw Exceptions with Meaningful Messages&lt;/h2>
&lt;p>When throwing exceptions, provide descriptive messages that help developers understand the cause of the error. This makes debugging and troubleshooting much easier.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">if&lt;/span> (input == &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentNullException(nameof(input), &lt;span style="color:#e6db74">&amp;#34;Input cannot be null.&amp;#34;&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="avoid-swallowing-exceptions">Avoid Swallowing Exceptions&lt;/h2>
&lt;p>Avoid situations where exceptions are caught but not properly handled. If you can&amp;rsquo;t handle an exception at a certain level, it&amp;rsquo;s better to let it propagate up the call stack.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#75715e">// Code that may throw an exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (Exception ex)
{
&lt;span style="color:#75715e">// Don&amp;#39;t just ignore the exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// Log it or rethrow if necessary
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Log.Error(ex.Message);
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="use-custom-exceptions-for-specific-cases">Use Custom Exceptions for Specific Cases&lt;/h2>
&lt;p>Creating custom exceptions can provide clarity about the nature of the error. It also allows you to add specific properties or methods relevant to your application.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">CustomException&lt;/span> : Exception
{
&lt;span style="color:#66d9ef">public&lt;/span> CustomException(&lt;span style="color:#66d9ef">string&lt;/span> message) : &lt;span style="color:#66d9ef">base&lt;/span>(message) { }
&lt;span style="color:#75715e">// Additional custom properties or methods can be added here
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="log-exceptions">Log Exceptions&lt;/h2>
&lt;p>Logging exceptions is crucial for debugging and monitoring the health of your application. Use a logging framework to record details about the exception, such as the message, stack trace, and context.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#75715e">// Code that may throw an exception
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (Exception ex)
{
Log.Error(&lt;span style="color:#e6db74">$&amp;#34;An error occurred: {ex.Message}&amp;#34;&lt;/span>, ex);
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Effective exception handling is an integral part of writing reliable and maintainable code in C#. By following these best practices, you can build applications that gracefully handle errors, making them more robust and user-friendly. Remember, the goal is not just to catch exceptions, but to handle them in a way that promotes the stability and reliability of your software. Happy coding!&lt;/p></description></item><item><title>Effective Code Reviews in a Development Team</title><link>https://www.darrenhorrocks.co.uk/effective-code-reviews-development-team/</link><pubDate>Tue, 17 Oct 2023 08:28:37 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/effective-code-reviews-development-team/</guid><description>&lt;p>Code reviews are a crucial part of the software development process. They serve as a quality assurance measure, promote knowledge sharing, and enhance team collaboration. However, not all code reviews are equally effective. In this article, we&amp;rsquo;ll delve into the best practices for conducting productive and efficient code reviews within a development team.&lt;/p>
&lt;h2 id="set-clear-objectives">Set Clear Objectives&lt;/h2>
&lt;p>Before diving into a code review, it&amp;rsquo;s important to define the specific goals and objectives. Are you primarily looking for bugs? Are you assessing adherence to coding standards? Clearly communicating the review&amp;rsquo;s purpose sets the right expectations for everyone involved.&lt;/p>
&lt;h2 id="review-small-chunks-of-code">Review Small Chunks of Code&lt;/h2>
&lt;p>Breaking down code into smaller, manageable chunks ensures that reviewers can focus on each piece effectively. It&amp;rsquo;s easier to catch errors and provide meaningful feedback when dealing with concise sections of code.&lt;/p>
&lt;h2 id="automate-what-you-can">Automate What You Can&lt;/h2>
&lt;p>Automated tools and static code analyzers can catch many common issues, leaving more room for reviewers to focus on higher-level concerns. Use linters, code formatters, and other automated checks to streamline the review process.&lt;/p>
&lt;h2 id="provide-constructive-feedback">Provide Constructive Feedback&lt;/h2>
&lt;p>When offering feedback, be specific and constructive. Point out what was done well, and suggest improvements where necessary. Avoid personal critiques and focus on the code itself.&lt;/p>
&lt;h2 id="encourage-discussions-not-arguments">Encourage Discussions, Not Arguments&lt;/h2>
&lt;p>Code reviews should be a collaborative effort. Encourage open discussions about the code. If there are disagreements, aim for a consensus rather than a winner-takes-all scenario. The goal is to improve the code, not to prove who&amp;rsquo;s right.&lt;/p>
&lt;h2 id="follow-coding-standards-and-guidelines">Follow Coding Standards and Guidelines&lt;/h2>
&lt;p>Consistency in code style and structure is crucial for maintainability. Establish clear coding standards and ensure that everyone on the team follows them. Code reviews are an excellent opportunity to reinforce these standards.&lt;/p>
&lt;h2 id="balance-speed-with-thoroughness">Balance Speed with Thoroughness&lt;/h2>
&lt;p>While it&amp;rsquo;s important to review code promptly, rushing through the process can lead to oversight. Strive for a balance between speed and thoroughness. Set realistic timeframes for reviews to maintain a steady development pace.&lt;/p>
&lt;h2 id="track-and-learn-from-mistakes">Track and Learn from Mistakes&lt;/h2>
&lt;p>Mistakes and oversights happen. Use them as learning opportunities. Implement measures to track and analyze the types of issues that arise during code reviews. This feedback loop can lead to process improvements over time.&lt;/p>
&lt;h2 id="rotate-reviewers">Rotate Reviewers&lt;/h2>
&lt;p>Having different team members review code brings fresh perspectives and can uncover issues that might have been missed by others. It also spreads knowledge across the team, making everyone more familiar with the codebase.&lt;/p>
&lt;h2 id="document-and-archive-reviews">Document and Archive Reviews&lt;/h2>
&lt;p>Keep records of code reviews for future reference. Documenting feedback and decisions can help with onboarding new team members, troubleshooting, and providing a historical context for code changes.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Effective code reviews are a cornerstone of successful software development teams. They foster a culture of continuous improvement, enhance code quality, and facilitate knowledge sharing. By following these best practices, your team can conduct code reviews that lead to better, more maintainable code.&lt;/p>
&lt;p>Remember, code reviews are not just about finding bugs; they&amp;rsquo;re about making the entire development process more efficient and collaborative. Embrace them as an integral part of your team&amp;rsquo;s workflow. Happy reviewing!&lt;/p></description></item><item><title>Building a Cross-Platform Desktop App with .NET MAUI</title><link>https://www.darrenhorrocks.co.uk/building-crossplatform-desktop-app-with-net-maui/</link><pubDate>Mon, 16 Oct 2023 09:22:56 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/building-crossplatform-desktop-app-with-net-maui/</guid><description>&lt;p>.NET MAUI (Multi-platform App UI) is a powerful framework that empowers developers to build cross-platform applications with ease. In this article, we&amp;rsquo;ll guide you through the process of creating a desktop app using .NET MAUI.&lt;/p>
&lt;h2 id="what-is-net-maui">What is .NET MAUI?&lt;/h2>
&lt;p>.NET MAUI is a modern, open-source UI framework from Microsoft that allows you to create applications for Android, iOS, macOS, and Windows from a single codebase. It leverages the power of Xamarin.Forms and extends it to cover more platforms.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>Before we dive in, make sure you have the following installed:&lt;/p>
&lt;ul>
&lt;li>Visual Studio 2022 (or later)&lt;/li>
&lt;li>.NET MAUI workload installed with Visual Studio.&lt;/li>
&lt;/ul>
&lt;h2 id="step-1-creating-a-new-net-maui-project">Step 1: Creating a New .NET MAUI Project&lt;/h2>
&lt;ul>
&lt;li>Open Visual Studio and select &amp;ldquo;Create a new project.&amp;rdquo;&lt;/li>
&lt;li>Choose &amp;ldquo;MAUI App&amp;rdquo; template and click &amp;ldquo;Next.&amp;rdquo;&lt;/li>
&lt;li>Provide a project name and location, then click &amp;ldquo;Create.&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="step-2-designing-the-user-interface">Step 2: Designing the User Interface&lt;/h2>
&lt;p>.NET MAUI uses XAML for designing user interfaces, making it easy to create visually appealing layouts. Add pages, controls, and customize properties using XAML markup.&lt;/p>
&lt;h2 id="step-3-writing-code-in-c">Step 3: Writing Code in C#&lt;/h2>
&lt;p>Since .NET MAUI is based on .NET, you can use C# to implement the app&amp;rsquo;s logic. Utilize the power of C# to handle events, perform calculations, and interact with APIs.&lt;/p>
&lt;h2 id="step-4-handling-platform-specific-code">Step 4: Handling Platform-Specific Code&lt;/h2>
&lt;p>.NET MAUI allows you to implement platform-specific code when needed. This ensures that your app can take full advantage of each platform&amp;rsquo;s unique capabilities.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">if&lt;/span> (Device.RuntimePlatform == Device.iOS)
{
&lt;span style="color:#75715e">// iOS-specific code
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">else&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (Device.RuntimePlatform == Device.Android)
{
&lt;span style="color:#75715e">// Android-specific code
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#75715e">// Add more platform-specific code as needed
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-5-debugging-and-testing">Step 5: Debugging and Testing&lt;/h2>
&lt;p>Use Visual Studio&amp;rsquo;s debugging tools to identify and fix issues in your code. Leverage the built-in emulator or connect physical devices for testing.&lt;/p>
&lt;h2 id="step-6-deploying-your-app">Step 6: Deploying Your App&lt;/h2>
&lt;p>Once your app is ready, it&amp;rsquo;s time to deploy it. .NET MAUI allows you to publish your app to various app stores or distribute it through other channels.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>With .NET MAUI, building cross-platform desktop apps has never been easier. Its unified codebase, powerful UI capabilities, and support for platform-specific code make it a top choice for developers aiming to reach a broad audience. Start your .NET MAUI journey today and bring your ideas to life on Android, iOS, macOS, and Windows!&lt;/p>
&lt;p>Remember, this is just the beginning. As you delve deeper into .NET MAUI, you&amp;rsquo;ll discover even more features and capabilities that will help you create truly exceptional cross-platform desktop applications.&lt;/p></description></item><item><title>Efficient Debugging with Visual Studio</title><link>https://www.darrenhorrocks.co.uk/efficient-debugging-visual-studio/</link><pubDate>Sat, 14 Oct 2023 09:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/efficient-debugging-visual-studio/</guid><description>&lt;p>Debugging in C# is not about removing bugs; it&amp;rsquo;s the process of understanding and isolating them before removing them.&lt;/p>
&lt;p>In the world of C# development, debugging is akin to being a detective. It&amp;rsquo;s the skill that separates a good developer from a great one. No matter how skilled you are in writing code, bugs are inevitable companions in the journey of software creation. In this article, we&amp;rsquo;ll explore the art of debugging in C#, uncovering techniques that can streamline your problem-solving process and lead to more efficient, error-free code, with the help of Visual Studio.&lt;/p>
&lt;h2 id="understanding-the-debugging-mindset">Understanding the Debugging Mindset&lt;/h2>
&lt;p>Before diving into specific techniques, it&amp;rsquo;s crucial to adopt the right mindset when approaching debugging:&lt;/p>
&lt;h3 id="patience-is-a-virtue">Patience is a Virtue&lt;/h3>
&lt;p>Debugging can be a time-consuming process. It&amp;rsquo;s important to approach it with patience and persistence. The most elusive bugs often require careful examination and creative thinking.&lt;/p>
&lt;h3 id="isolation-and-reproducibility">Isolation and Reproducibility&lt;/h3>
&lt;p>The first step in debugging is isolating the problem. Reproduce the issue in a controlled environment. This ensures you&amp;rsquo;re working with a consistent set of circumstances, making it easier to identify the root cause.&lt;/p>
&lt;h3 id="documentation-is-key">Documentation is Key&lt;/h3>
&lt;p>Keep detailed records of your debugging process. Note what you&amp;rsquo;ve tried, what worked, and what didn&amp;rsquo;t. This serves as a valuable resource for future debugging endeavors.&lt;/p>
&lt;h2 id="techniques-for-efficient-debugging-in-c">Techniques for Efficient Debugging in C#&lt;/h2>
&lt;h3 id="print-debugging-in-c-with-consolewriteline">Print Debugging in C# with Console.WriteLine()&lt;/h3>
&lt;p>One of the oldest and most straightforward methods in C# is print debugging. It involves strategically placing &lt;code>Console.WriteLine()&lt;/code> statements in your code to output relevant information. This can help track the flow of execution and identify unexpected behaviors.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> MyFunction(&lt;span style="color:#66d9ef">int&lt;/span> arg)
{
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;arg: {arg}&amp;#34;&lt;/span>); &lt;span style="color:#75715e">// Add print statements
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> result = arg * &lt;span style="color:#ae81ff">2&lt;/span>;
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;result: {result}&amp;#34;&lt;/span>); &lt;span style="color:#75715e">// Add print statements
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> result;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="utilize-visual-studios-debugging-tools">Utilize Visual Studio&amp;rsquo;s Debugging Tools&lt;/h3>
&lt;p>Visual Studio provides powerful debugging tools for C# development. These tools allow you to set breakpoints, inspect variables, and step through your code line by line. Familiarize yourself with the debugging features of Visual Studio for a more efficient debugging experience.&lt;/p>
&lt;h3 id="rubber-duck-debugging-in-c">Rubber Duck Debugging in C#&lt;/h3>
&lt;p>Sometimes, the act of explaining the problem to someone (or something) else can lead to a breakthrough. This is the essence of rubber duck debugging. Simply verbalizing the issue can provide new insights and perspectives.&lt;/p>
&lt;h3 id="4-binary-search-method-in-c">4. Binary Search Method in C#&lt;/h3>
&lt;p>If dealing with a large codebase, the binary search method can be incredibly efficient in C#. Divide the code in half and determine which half contains the bug. Continue narrowing down until you&amp;rsquo;ve pinpointed the problematic section.&lt;/p>
&lt;h3 id="code-review-and-pair-programming-in-c">Code Review and Pair Programming in C#&lt;/h3>
&lt;p>Another set of eyes can be invaluable in the debugging process in C#. A fresh perspective might catch something you&amp;rsquo;ve overlooked. Code reviews and pair programming can uncover issues early in the development process.&lt;/p>
&lt;h3 id="regression-testing-in-c">Regression Testing in C#&lt;/h3>
&lt;p>If you&amp;rsquo;ve fixed a bug in C#, it&amp;rsquo;s essential to ensure that the fix hasn&amp;rsquo;t introduced new issues. Regression testing involves re-running previous tests to confirm that existing functionality remains intact.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Debugging in C# with Visual Studio is a skill that grows with experience. The more you encounter and resolve bugs, the more adept you become at spotting and solving them. Remember, it&amp;rsquo;s not just about fixing the immediate issue, but also about understanding why it occurred in the first place. With practice and the right mindset, you&amp;rsquo;ll master the art of debugging and become a more proficient C# developer in Visual Studio.&lt;/p>
&lt;p>So, the next time you encounter a bug, don your detective hat, and embark on the exhilarating journey of debugging in C#! Happy coding!&lt;/p></description></item><item><title>Unit Testing vs Manual Testing</title><link>https://www.darrenhorrocks.co.uk/unit-testing-vs-manual-testing/</link><pubDate>Fri, 13 Oct 2023 22:24:09 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/unit-testing-vs-manual-testing/</guid><description>&lt;p>Selecting the right testing methodology can significantly impact the quality and reliability of your code. Two primary approaches, unit testing and manual testing, each have their strengths and are best applied in specific scenarios. Understanding when to employ each method is crucial for achieving a well-rounded testing strategy.&lt;/p>
&lt;h2 id="unit-testing-precision-in-isolation">Unit Testing: Precision in Isolation&lt;/h2>
&lt;p>Unit testing involves the examination of individual units or components of a software application to validate their correctness. These units are typically functions, methods, or classes. The key advantages of unit testing include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Early Bug Detection:&lt;/strong> Unit tests catch defects in the early stages of development, reducing the cost and effort required to rectify them.&lt;/li>
&lt;li>&lt;strong>Maintainable Codebase:&lt;/strong> They serve as documentation for how code is intended to be used, enhancing code comprehension and maintainability.&lt;/li>
&lt;li>&lt;strong>Regression Safety Net:&lt;/strong> Unit tests provide a safety net when making changes or adding new features, ensuring existing functionality remains intact.&lt;/li>
&lt;li>&lt;strong>Design Improvement:&lt;/strong> Writing unit tests promotes modular, loosely-coupled code, leading to better software architecture.&lt;/li>
&lt;li>&lt;strong>Confident Refactoring:&lt;/strong> Developers can make significant changes to the codebase with confidence, knowing that they will be alerted if anything breaks.&lt;/li>
&lt;/ul>
&lt;h3 id="best-use-cases-for-unit-testing-in-c">Best Use Cases for Unit Testing in C#&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Business Logic:&lt;/strong> Verify the correctness of algorithms, calculations, and data manipulations.&lt;/li>
&lt;li>&lt;strong>Isolated Functions/Methods:&lt;/strong> Test functions or methods that don&amp;rsquo;t rely heavily on external dependencies.&lt;/li>
&lt;li>&lt;strong>Core Libraries and Frameworks:&lt;/strong> Ensure that foundational components function as expected.&lt;/li>
&lt;/ul>
&lt;h2 id="manual-testing-the-human-touch">Manual Testing: The Human Touch&lt;/h2>
&lt;p>Manual testing involves human intervention to evaluate an application&amp;rsquo;s functionality, usability, and overall user experience. This approach is essential for aspects that automated tests might not cover. The key advantages of manual testing include:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>User Experience Assessment:&lt;/strong> Testers provide insights into the user-friendliness and intuitiveness of the software, identifying areas for improvement.&lt;/li>
&lt;li>&lt;strong>Exploratory Testing:&lt;/strong> Testers creatively explore the application, simulating real-world usage scenarios.&lt;/li>
&lt;li>&lt;strong>Non-Functional Testing:&lt;/strong> Performance, security, and compatibility testing are areas where manual testing shines.&lt;/li>
&lt;li>&lt;strong>Ad-Hoc Testing:&lt;/strong> Identifying unexpected issues that automated tests may not account for.&lt;/li>
&lt;li>&lt;strong>Usability Testing:&lt;/strong> Evaluating how user interfaces and interactions align with user expectations.&lt;/li>
&lt;/ul>
&lt;h2 id="best-use-cases-for-manual-testing-in-c">Best Use Cases for Manual Testing in C#&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>User Interface (UI) Testing:&lt;/strong> Evaluate the user interface for intuitiveness, responsiveness, and visual consistency.&lt;/li>
&lt;li>&lt;strong>User Experience (UX) Evaluation:&lt;/strong> Assess how the software aligns with user expectations and ease of use.&lt;/li>
&lt;li>&lt;strong>Security and Compliance Testing:&lt;/strong> Identify vulnerabilities, compliance with industry standards, and data protection measures.&lt;/li>
&lt;li>&lt;strong>Load and Performance Testing:&lt;/strong> Simulate real-world usage scenarios to evaluate system response under stress.&lt;/li>
&lt;li>&lt;strong>Compatibility Testing:&lt;/strong> Verify that the software functions correctly across different platforms and devices.&lt;/li>
&lt;/ul>
&lt;h2 id="balancing-unit-and-manual-testing">Balancing Unit and Manual Testing&lt;/h2>
&lt;p>The optimal testing strategy often involves a blend of both unit and manual testing. They complement each other, covering different aspects of testing. Unit tests ensure precise functionality, while manual tests offer a human perspective on user experience and non-functional aspects.&lt;/p>
&lt;p>By strategically applying unit and manual testing in C# development, you can elevate the quality and reliability of your software, providing users with a seamless and robust experience. Remember, the choice between these testing approaches should be based on the specific requirements and objectives of your project.&lt;/p></description></item><item><title>Essential Things Every Professional Software Developer Should Know</title><link>https://www.darrenhorrocks.co.uk/essential-things-every-professional-software-developer-should-know/</link><pubDate>Fri, 06 Oct 2023 09:34:35 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/essential-things-every-professional-software-developer-should-know/</guid><description>&lt;p>Greetings, aspiring software developers! As you embark on the path of professional software development, it is imperative to equip yourself with a solid foundation in key principles. Whether you are a seasoned coder or just beginning your journey, these fundamental concepts will serve as your guiding light in this dynamic field. Let&amp;rsquo;s delve into the essentials.&lt;/p>
&lt;h2 id="master-your-coding-arsenal">Master Your Coding Arsenal&lt;/h2>
&lt;p>Start by getting familiar with your chosen programming language(s) and the tools of the trade. For web developers, this typically involves languages like HTML, CSS, and JavaScript. If you&amp;rsquo;re venturing into AI, Python might be your go-to language.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.developernation.net/blog/tips-for-choosing-a-programming-language-for-your-it-career-projects">Choosing the Right Programming Language&lt;/a>&lt;/p>
&lt;h2 id="unleash-your-inner-problem-solver">Unleash Your Inner Problem-Solver&lt;/h2>
&lt;p>Software development is akin to solving a puzzle. You need to approach problems with a structured, algorithmic mindset. Break down complex issues into manageable steps, and you&amp;rsquo;re well on your way!&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.freecodecamp.org/news/how-to-think-like-a-programmer-lessons-in-problem-solving-d1d8bf1de7d2/">How to Think Like a Programmer&lt;/a>&lt;/p>
&lt;h2 id="dance-with-data-structures-and-algorithms">Dance with Data Structures and Algorithms&lt;/h2>
&lt;p>These are the backbone of any skilled coder. Arrays, linked lists, trees - make them your companions. Additionally, befriend sorting and searching algorithms; they&amp;rsquo;ll serve you well.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.geeksforgeeks.org/data-structures/">Data Structures and Algorithms Crash Course&lt;/a>&lt;/p>
&lt;h2 id="navigate-the-software-development-life-cycle-sdlc">Navigate the Software Development Life Cycle (SDLC)&lt;/h2>
&lt;p>Understanding how software evolves, from inception to coding, testing, and launch, is essential. Familiarise yourself with the Software Development Life Cycle to witness the magic unfold.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.synopsys.com/glossary/what-is-sdlc.html">Understanding the Software Development Lifecycle&lt;/a>&lt;/p>
&lt;h2 id="talk-the-talk-and-walk-the-walk">Talk the Talk and Walk the Walk&lt;/h2>
&lt;p>In the world of code, effective communication is paramount. Whether you&amp;rsquo;re explaining a complex concept to a non-techie or collaborating with your team, being a great communicator sets you apart.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://hackernoon.com/10-soft-skills-every-developer-needs-66f0cdcfd3f7">10 Communication Skills Every Developer Should Master&lt;/a>&lt;/p>
&lt;h2 id="lock-down-your-code-fort">Lock Down Your Code Fort&lt;/h2>
&lt;p>Security is paramount. Familiarise yourself with secure coding practices, robust authentication methods, and how to safeguard your code from malicious intent.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/">OWASP Secure Coding Practices&lt;/a>&lt;/p>
&lt;h2 id="need-for-speed-optimise-your-code">Need for Speed? Optimise Your Code&lt;/h2>
&lt;p>Efficiency matters. Learn how to optimise your code for speed and performance. Profiling tools and optimization techniques should be in your toolkit.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.stevejgordon.co.uk/motivations-for-writing-high-performance-csharp-code">Writing High-Performance Code&lt;/a>&lt;/p>
&lt;h2 id="put-your-code-to-the-test">Put Your Code to the Test&lt;/h2>
&lt;p>Thorough testing is a cornerstone of robust code. Master writing tests and using testing frameworks to ensure your code stands up to scrutiny.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.tutorialspoint.com/software_testing/index.htm">Software Testing Fundamentals&lt;/a>&lt;/p>
&lt;h2 id="git-gud-with-version-control">Git Gud with Version Control&lt;/h2>
&lt;p>Version control, facilitated by tools like Git and platforms like GitHub, is the linchpin of collaborative development. Familiarise yourself with branching, merging, and conflict resolution.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://learngitbranching.js.org/">Learn Git Branching&lt;/a>&lt;/p>
&lt;h2 id="automate-all-the-things-with-cicd">Automate All the Things with CI/CD!&lt;/h2>
&lt;p>CI/CD pipelines are your ticket to streamlined development. Learn how to automate your builds and deployments for a smoother coding experience.&lt;/p>
&lt;p>&lt;strong>Learn more:&lt;/strong> &lt;a href="https://www.logicmonitor.com/blog/introduction-to-ci-cd">Introduction to CI/CD&lt;/a>&lt;/p></description></item><item><title>Comparing GraphQL and OData: Which is Best and When?</title><link>https://www.darrenhorrocks.co.uk/comparing-graphql-odata/</link><pubDate>Thu, 05 Oct 2023 10:24:40 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/comparing-graphql-odata/</guid><description>&lt;p>In the realm of API query languages, GraphQL and OData stand out as powerful tools that facilitate data retrieval and manipulation. While both serve similar purposes, they exhibit distinct characteristics that cater to different use cases and development paradigms. In this article, we&amp;rsquo;ll delve into the key differences and similarities between GraphQL and OData, supported by examples to illustrate their unique features.&lt;/p>
&lt;h2 id="similarities">Similarities&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Query Language for APIs:&lt;/strong> Both GraphQL and OData serve as query languages for APIs, enabling clients to request specific data from a server.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Flexibility:&lt;/strong> Both allow clients to specify exactly which fields they want to retrieve, reducing over-fetching of data and improving performance.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Hierarchical Structure:&lt;/strong> Both follow a hierarchical structure, making it easy to navigate and request related data.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="differences">Differences:&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Schema Definition&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>GraphQL&lt;/strong> requires the explicit definition of a schema, which outlines the types of data that can be queried.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">User&lt;/span> {
id: &lt;span style="color:#a6e22e">ID&lt;/span>!
name: &lt;span style="color:#a6e22e">String&lt;/span>!
email: &lt;span style="color:#a6e22e">String&lt;/span>!
}
&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">Query&lt;/span> {
user(id: &lt;span style="color:#a6e22e">ID&lt;/span>!): &lt;span style="color:#a6e22e">User&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>OData&lt;/strong> uses the Entity Data Model (EDM) to define the data model, including entities and relationships.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;EntityType&lt;/span> &lt;span style="color:#a6e22e">Name=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;User&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;Key&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyRef&lt;/span> &lt;span style="color:#a6e22e">Name=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Id&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Key&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;Property&lt;/span> &lt;span style="color:#a6e22e">Name=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Id&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">Type=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Edm.Int32&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">Nullable=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;false&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;Property&lt;/span> &lt;span style="color:#a6e22e">Name=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Name&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">Type=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Edm.String&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;Property&lt;/span> &lt;span style="color:#a6e22e">Name=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Email&amp;#34;&lt;/span> &lt;span style="color:#a6e22e">Type=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Edm.String&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/EntityType&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Query Language&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>GraphQL&lt;/strong> uses a specific query language that allows clients to request exactly what they need.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">&lt;span style="color:#66d9ef">query&lt;/span> {
&lt;span style="color:#a6e22e">user&lt;/span>(id: &lt;span style="color:#e6db74">&amp;#34;1&amp;#34;&lt;/span>) {
&lt;span style="color:#a6e22e">name&lt;/span>
email
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>OData&lt;/strong> relies on URL conventions for querying data&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">GET /Users(1)?$select=Name,Email
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Complex Queries&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>GraphQL&lt;/strong> excels at handling complex queries, allowing clients to traverse deeply nested data structures.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">&lt;span style="color:#66d9ef">query&lt;/span> {
&lt;span style="color:#a6e22e">user&lt;/span>(id: &lt;span style="color:#e6db74">&amp;#34;1&amp;#34;&lt;/span>) {
&lt;span style="color:#a6e22e">name&lt;/span>
posts {
title
comments {
text
}
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>OData&lt;/strong> supports complex queries, but it may require more verbose URLs.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">GET /Users(1)?$expand=Posts($expand=Comments($select=Text)),Name,Email
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Filtering and Sorting&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>GraphQL&lt;/strong> typically requires custom resolver logic for filtering and sorting&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-graphql" data-lang="graphql">&lt;span style="color:#66d9ef">query&lt;/span> {
&lt;span style="color:#a6e22e">users&lt;/span>(filter: {&lt;span style="color:#a6e22e">name_contains&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;John&amp;#34;&lt;/span>}, &lt;span style="color:#a6e22e">orderBy&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;name&amp;#34;&lt;/span>) {
&lt;span style="color:#a6e22e">name&lt;/span>
email
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>&lt;strong>OData&lt;/strong> provides built-in query options for filtering, sorting, and paging.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">GET /Users?$filter=contains(Name,&amp;#39;John&amp;#39;)&lt;span style="color:#960050;background-color:#1e0010">&amp;amp;&lt;/span>$orderby=Name asc
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Metadata&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>GraphQL&lt;/strong> does not have built-in metadata endpoints&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>OData&lt;/strong> exposes metadata, allowing clients to dynamically discover the structure of the API.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">GET /$metadata
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Batch Operations&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>GraphQL&lt;/strong> does not have built-in support for batch operations&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>OData&lt;/strong> supports batch requests, allowing multiple operations to be grouped into a single HTTP request&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">POST /$batch
&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="when-to-use-graphql">When to Use GraphQL:&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Complex Data Relationships:&lt;/strong> Use GraphQL when dealing with complex relationships between data entities, as it allows for efficient traversal of nested data structures.&lt;/li>
&lt;li>&lt;strong>Frontend-Driven Development:&lt;/strong> GraphQL is an excellent choice when the frontend team has a strong influence on data requirements, as it enables them to request exactly the data they need.&lt;/li>
&lt;li>&lt;strong>Real-Time Data Needs:&lt;/strong> If real-time data updates are crucial, GraphQL subscriptions provide a powerful mechanism for real-time data synchronization between the server and clients.&lt;/li>
&lt;li>&lt;strong>Single Page Applications (SPAs):&lt;/strong> For SPAs where optimizing network requests is crucial, GraphQL can reduce over-fetching of data, improving performance.&lt;/li>
&lt;li>&lt;strong>Microservices Architecture:&lt;/strong> GraphQL is well-suited for microservices environments, where different services may expose different types of data, allowing clients to aggregate information efficiently.&lt;/li>
&lt;li>&lt;strong>Mobile Applications:&lt;/strong> In mobile applications, where minimizing data transfer is critical for performance and battery life, GraphQL&amp;rsquo;s fine-grained control over data retrieval is advantageous.&lt;/li>
&lt;/ul>
&lt;h2 id="when-to-use-odata">When to Use OData&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Enterprise-Level Applications:&lt;/strong> In large enterprise applications with existing OData services or legacy systems that support OData, it may be more straightforward to continue using OData.&lt;/li>
&lt;li>&lt;strong>Query Optimization and Caching:&lt;/strong> OData&amp;rsquo;s query options for filtering, sorting, and paging can be highly efficient, especially when working with large datasets. Additionally, caching can be effectively implemented.&lt;/li>
&lt;li>&lt;strong>Metadata-Driven Development:&lt;/strong> OData&amp;rsquo;s metadata capabilities are valuable when dynamic discovery of API structure is needed.&lt;/li>
&lt;li>&lt;strong>Batch Operations:&lt;/strong> In scenarios where batch operations are essential, OData provides built-in support, simplifying complex data operations.&lt;/li>
&lt;li>&lt;strong>Strongly-Typed Environments:&lt;/strong> In environments where a strongly-typed data model is preferred, OData&amp;rsquo;s use of the Entity Data Model (EDM) aligns well with this paradigm.&lt;/li>
&lt;li>&lt;strong>Existing OData Ecosystem:&lt;/strong> When integrating with existing systems, services, or ecosystems that already support OData, it makes sense to continue using OData to leverage existing infrastructure.&lt;/li>
&lt;/ul>
&lt;h2 id="choosing-the-right-tool">Choosing the Right Tool&lt;/h2>
&lt;p>Ultimately, the choice between GraphQL and OData depends on various factors including project requirements, team expertise, and existing infrastructure. It&amp;rsquo;s essential to evaluate the specific needs of the application and consider how each technology aligns with those needs. Additionally, hybrid approaches are also feasible, where GraphQL and OData can be used in different parts of an application to leverage the strengths of both.&lt;/p></description></item><item><title>Why Your Language Choice Doesn't Matter as Good Programmer</title><link>https://www.darrenhorrocks.co.uk/why-your-language-choice-doesnt-matter-as-good-programmer/</link><pubDate>Tue, 03 Oct 2023 09:26:20 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-your-language-choice-doesnt-matter-as-good-programmer/</guid><description>&lt;p>In the world of coding, there&amp;rsquo;s often a lively debate about which programming language reigns supreme. However, the real measure of a top-notch programmer isn&amp;rsquo;t their expertise in just one language, but their knack for seamlessly shifting between them. In this article, we&amp;rsquo;ll explore why it&amp;rsquo;s more important for a skilled coder to be adaptable rather than fixated on a single language.&lt;/p>
&lt;h2 id="grasping-the-core-ideas">Grasping the Core Ideas&lt;/h2>
&lt;p>A good programmer knows that the heart of coding—things like how data&amp;rsquo;s organized, strategies for solving problems, and the building blocks of software—are universal. These principles aren&amp;rsquo;t confined to a particular language; they form the bedrock on which all programming languages are built. By mastering these basics, a seasoned coder can easily apply their knowledge to any language.&lt;/p>
&lt;h2 id="picking-the-right-tool-for-the-job">Picking the Right Tool for the Job&lt;/h2>
&lt;p>Different languages have different strengths and are best suited to particular tasks. For instance, Python&amp;rsquo;s fantastic for number crunching and machine learning, while C++ is a champ in low-level system work. A savvy programmer understands the importance of choosing the right language for the job, instead of trying to force a solution into a language that might not be the best fit.&lt;/p>
&lt;h2 id="being-adaptable-shows-true-skill">Being Adaptable Shows True Skill&lt;/h2>
&lt;p>Being good at multiple programming languages is a testament to a programmer&amp;rsquo;s ability to adapt and be versatile. It&amp;rsquo;s a sign of someone who can learn fast, think critically, and tackle problems with finesse. This adaptability goes beyond just languages; it extends to being comfortable with different development tools, frameworks, and environments.&lt;/p>
&lt;h2 id="boosting-your-career-and-appeal-to-employers">Boosting Your Career and Appeal to Employers&lt;/h2>
&lt;p>In the job market, being a flexible programmer is what you need. Employers highly value developers who can smoothly transition between projects, technologies, and languages. This adaptability lets businesses respond swiftly to changing project needs and industry trends.&lt;/p>
&lt;h2 id="learning-new-languages-becomes-a-breeze">Learning New Languages Becomes a Breeze&lt;/h2>
&lt;p>Once you&amp;rsquo;ve got the core concepts of programming down, picking up a new language is mainly about getting the hang of its style and idioms. This process is much smoother for a programmer who&amp;rsquo;s already worked with a variety of languages. It&amp;rsquo;s like being fluent in several spoken languages; once you know how to learn a language, picking up new ones becomes second nature.&lt;/p>
&lt;h2 id="avoiding-stubbornness-welcoming-practicality">Avoiding Stubbornness, Welcoming Practicality&lt;/h2>
&lt;p>Getting too attached to one language can cause stubbornness, where a programmer insists on using their favorite language even when it might not be the best fit for a particular project. This rigid mindset can slow progress and stifle innovation. A wise programmer is practical, recognizing that the best solution might call for adopting a different language.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>In software development, the ability to adapt in different coding environments is what sets a skilled programmer apart from the rest. By focusing on core concepts, choosing the right tool for each job, and being open to learning new languages, programmers can unlock new opportunities, enhance their appeal to employers, and ultimately become more effective and versatile developers. Remember, it&amp;rsquo;s not about the language you use, but how skillfully you use it.&lt;/p></description></item><item><title>Choosing the Right IDE: Visual Studio vs. JetBrains Rider</title><link>https://www.darrenhorrocks.co.uk/choosing-right-ide-visual-studio-vs-jetbrains-rider/</link><pubDate>Tue, 03 Oct 2023 08:49:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/choosing-right-ide-visual-studio-vs-jetbrains-rider/</guid><description>&lt;p>When it comes to software development, the choice of Integrated Development Environment (IDE) can greatly influence productivity and code quality. Two heavyweights in the IDE arena are Microsoft&amp;rsquo;s Visual Studio and JetBrains' Rider. In this article, we&amp;rsquo;ll delve into the strengths and weaknesses of each, helping you make an informed decision for your next project.&lt;/p>
&lt;h2 id="visual-studio-the-microsoft-powerhouse">Visual Studio: The Microsoft Powerhouse&lt;/h2>
&lt;h3 id="pros">Pros&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Wide Ecosystem&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Visual Studio boasts extensive language and platform support, from C# and .NET to Python and JavaScript. This versatility makes it an attractive choice for developers working in various tech stacks.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Intuitive Interface&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>The IDE&amp;rsquo;s user-friendly interface is designed for smooth navigation and a comfortable coding experience. This helps developers focus on writing quality code rather than struggling with the environment.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Strong Microsoft Integration&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>If your project is firmly rooted in the Microsoft ecosystem, Visual Studio is the natural choice. It seamlessly integrates with Azure, SQL Server, and other Microsoft technologies, streamlining the development process.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Powerful Debugging Tools&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Visual Studio sets the bar high with its robust debugging capabilities. Features like live debugging, IntelliTrace, and Performance Profiler empower developers to efficiently identify and squash bugs.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Rich Plugin Ecosystem&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>The Visual Studio Marketplace offers an extensive library of extensions. This allows developers to customize and extend the IDE&amp;rsquo;s functionality to suit their specific needs.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="cons">Cons&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Resource Intensive&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>On the flip side, Visual Studio can be resource-intensive, especially for larger projects. This may lead to slower performance on less powerful machines, necessitating higher hardware requirements.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Limited Cross-platform Support&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>While versatile, Visual Studio might not be the go-to choice for developers heavily invested in cross-platform development compared to some other IDEs.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="jetbrains-rider-a-cross-platform-marvel">JetBrains Rider: A Cross-Platform Marvel&lt;/h2>
&lt;h3 id="pros-1">Pros&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Cross-platform Support&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Rider is a cross-platform champion, designed to seamlessly run on Windows, macOS, and Linux. This flexibility accommodates teams with diverse operating system preferences.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Intelligent Code Assistance&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Rider stands out with its intelligent code completion, inspections, and refactoring tools. These features enhance productivity by streamlining the development workflow.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Multi-language Support&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Beyond its proficiency in C# and VB.NET, Rider extends its support to a wide array of languages including JavaScript, TypeScript, HTML, CSS, and more. This makes it a versatile choice for polyglot developers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Smooth Integration with JetBrains Tools&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Rider harmonizes effortlessly with other JetBrains tools such as ReSharper for C# and WebStorm for web development. This tight integration augments the IDE&amp;rsquo;s capabilities.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Efficient Performance and Memory Management&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>One of Rider&amp;rsquo;s standout features is its efficiency in terms of memory usage. This leads to snappier performance, particularly on less powerful hardware configurations.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="cons-1">Cons&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Learning Curve&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>For developers new to JetBrains IDEs, there might be a learning curve in getting accustomed to the interface and workflow. However, the investment is well worth the productivity gains in the long run.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Plugin Ecosystem&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>While Rider has a commendable selection of plugins, it may not rival Visual Studio&amp;rsquo;s extensive library. Nevertheless, it covers the essentials and offers a solid foundation for customization.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="conclusion-the-right-tool-for-the-right-job">Conclusion: The Right Tool for the Right Job&lt;/h2>
&lt;p>Ultimately, the choice between Visual Studio and JetBrains Rider hinges on your specific needs, preferences, and the nature of your development work. Both IDEs are formidable contenders, widely embraced by developers across the industry. So, rest assured, you&amp;rsquo;re in capable hands no matter which one you choose. Happy coding!&lt;/p></description></item><item><title>Embracing TypeScript: The Advantages Over Raw JavaScript</title><link>https://www.darrenhorrocks.co.uk/embracing-typescript-advantages-over-raw-javascript/</link><pubDate>Mon, 02 Oct 2023 08:41:59 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/embracing-typescript-advantages-over-raw-javascript/</guid><description>&lt;p>JavaScript has been the backbone of web development for decades, enabling dynamic and interactive web applications. However, as web projects grow in size and complexity, so do the challenges of managing code quality and catching potential bugs. Enter TypeScript, a superset of JavaScript that adds static typing and advanced tooling to the mix. In this article, we&amp;rsquo;ll explore why developers should consider using TypeScript over raw JavaScript for their projects.&lt;/p>
&lt;h2 id="strong-typing-for-enhanced-safety">Strong Typing for Enhanced Safety&lt;/h2>
&lt;p>One of the most compelling reasons to use TypeScript is its strong typing system. This allows developers to specify the type of data that variables, parameters, and functions can hold. This leads to fewer runtime errors and better code quality, as type-related issues are caught during development rather than at runtime.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-typescript" data-lang="typescript">&lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">greet&lt;/span>(&lt;span style="color:#a6e22e">name&lt;/span>: &lt;span style="color:#66d9ef">string&lt;/span>) {
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#e6db74">`Hello, &lt;/span>&lt;span style="color:#e6db74">${&lt;/span>&lt;span style="color:#a6e22e">name&lt;/span>&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">!`&lt;/span>;
}
&lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#a6e22e">message&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">greet&lt;/span>(&lt;span style="color:#ae81ff">42&lt;/span>); &lt;span style="color:#75715e">// Error: Argument of type &amp;#39;42&amp;#39; is not assignable to parameter of type &amp;#39;string&amp;#39;.
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="improved-code-maintainability">Improved Code Maintainability&lt;/h2>
&lt;p>As projects scale, maintaining a clear understanding of variable types and function signatures becomes increasingly challenging in JavaScript. TypeScript&amp;rsquo;s static type annotations serve as documentation that can be utilized by developers and IDEs to provide context and suggest appropriate completions. This makes it easier to navigate and understand large codebases.&lt;/p>
&lt;h2 id="enhanced-ide-support-and-tooling">Enhanced IDE Support and Tooling&lt;/h2>
&lt;p>TypeScript provides powerful tooling and IDE support. Popular editors like Visual Studio Code, WebStorm, and others offer extensive TypeScript integration, including features like autocompletion, refactoring tools, and immediate feedback on potential type errors. This not only boosts productivity but also helps catch and fix issues early in the development process.&lt;/p>
&lt;h2 id="better-collaboration-in-teams">Better Collaboration in Teams&lt;/h2>
&lt;p>In larger development teams, where multiple developers may be working on different parts of a codebase simultaneously, TypeScript helps facilitate smoother collaboration. With explicit type annotations, it&amp;rsquo;s easier for team members to understand each other&amp;rsquo;s code and integrate their work seamlessly.&lt;/p>
&lt;h2 id="enables-more-robust-testing">Enables More Robust Testing&lt;/h2>
&lt;p>Static typing is a powerful ally in testing. It allows for more accurate and efficient unit testing as developers can be sure that their functions receive and return the expected types of data. This leads to more effective testing strategies and helps prevent common runtime errors.&lt;/p>
&lt;h2 id="reduced-debugging-time">Reduced Debugging Time&lt;/h2>
&lt;p>TypeScript significantly reduces the time spent debugging runtime errors caused by type mismatches or undefined variables. The compiler catches many of these issues before the code even runs, leading to faster development cycles and more reliable applications.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>While JavaScript is a versatile language, TypeScript offers a powerful set of tools and features that can significantly improve the development process. From enhanced safety through static typing to improved maintainability, TypeScript has become a go-to choice for many developers working on modern web projects. By adopting TypeScript, developers can write more reliable, maintainable, and scalable code, ultimately leading to better user experiences and more robust applications.&lt;/p></description></item><item><title>Evaluating Go: Why It Might Not Always Be a Suitable Replacement for C++</title><link>https://www.darrenhorrocks.co.uk/evaluating-go-why-it-might-not-always-be-suitable-replacement-c/</link><pubDate>Sun, 01 Oct 2023 08:12:53 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/evaluating-go-why-it-might-not-always-be-suitable-replacement-c/</guid><description>&lt;p>Go, also known as Golang, has surged in popularity for its simplicity, efficiency, and concurrency model. While it excels in certain domains, it may not always be the optimal choice for replacing C++. In this article, we&amp;rsquo;ll delve into some of the reasons why Go might not be the perfect substitute for C++.&lt;/p>
&lt;h2 id="lack-of-low-level-capabilities">Lack of Low-Level Capabilities&lt;/h2>
&lt;p>C++ is renowned for its ability to work at a low-level, allowing for fine-grained control over system resources. Go, on the other hand, is designed with simplicity in mind, sacrificing some low-level capabilities. This can be a limiting factor in scenarios that require intricate memory management or direct hardware interaction, such as device drivers or embedded systems development.&lt;/p>
&lt;h2 id="limited-language-features">Limited Language Features&lt;/h2>
&lt;p>Go deliberately omits certain features found in languages like C++. It lacks generics, which can result in code duplication or less reusable components. Additionally, Go&amp;rsquo;s lack of operator overloading and its minimalist approach to object-oriented programming may be a stumbling block for developers accustomed to C++&amp;rsquo;s more expressive syntax.&lt;/p>
&lt;h2 id="limited-support-for-gui-development">Limited Support for GUI Development&lt;/h2>
&lt;p>C++ has a rich history of GUI development, with libraries like Qt and GTK providing robust solutions. Go, however, has a more nascent ecosystem in this regard. While there are libraries available for GUI development in Go, they may not offer the same level of maturity and feature set as their C++ counterparts.&lt;/p>
&lt;h2 id="performance-overheads">Performance Overheads&lt;/h2>
&lt;p>Go&amp;rsquo;s garbage collector, while efficient for many applications, can introduce non-deterministic delays in real-time or performance-critical systems. In contrast, C++ allows for explicit memory management, giving developers more control over performance optimizations. This can be a crucial consideration in domains where predictable performance is paramount.&lt;/p>
&lt;h2 id="limited-support-for-native-libraries">Limited Support for Native Libraries&lt;/h2>
&lt;p>C++ has a seamless integration with C, allowing for direct use of native libraries and system APIs. Go, while it provides mechanisms for interfacing with C code, may require additional effort to interact with existing C or C++ libraries. This can be a significant hurdle for projects that heavily rely on established C++ libraries or have dependencies on native system interfaces.&lt;/p>
&lt;h2 id="maturity-and-ecosystem">Maturity and Ecosystem&lt;/h2>
&lt;p>C++ has a decades-long history and a mature ecosystem with a vast array of libraries, frameworks, and tools. Go, while growing rapidly, may not have the same level of diversity and depth in its ecosystem. This can be a critical factor for projects that rely heavily on third-party libraries or need access to a wide range of specialised tools.&lt;/p>
&lt;p>In summary, while Go has many merits and excels in certain areas, it is not always a direct replacement for C++. It&amp;rsquo;s essential to carefully evaluate the specific requirements and constraints of a project before deciding on a programming language. In some cases, the unique strengths of C++, such as low-level capabilities and a mature ecosystem, may outweigh the benefits of Go&amp;rsquo;s simplicity and efficiency. The choice between C++ and Go should be driven by a thorough understanding of the project&amp;rsquo;s needs and the strengths of each language.&lt;/p></description></item><item><title>Debunking the Rust Hype: Why It's Not Always a Good Replacement for C++</title><link>https://www.darrenhorrocks.co.uk/debunking-rust-hype-why-its-not-always-good-replacement-c/</link><pubDate>Sat, 30 Sep 2023 08:10:13 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/debunking-rust-hype-why-its-not-always-good-replacement-c/</guid><description>&lt;p>In recent years, Rust has gained significant attention and popularity in the programming community. Advocates often tout it as a worthy successor to C++, promising safer and more efficient systems programming. While Rust certainly has its merits, it&amp;rsquo;s essential to recognise that it may not always be the best fit for every scenario. In this article, we&amp;rsquo;ll explore some of the reasons why Rust may not necessarily be a superior replacement for C++.&lt;/p>
&lt;h2 id="learning-curve">Learning Curve&lt;/h2>
&lt;p>One of the most significant challenges when adopting Rust is its steep learning curve. C++ developers, especially those with extensive experience, might find it particularly challenging to transition to Rust due to its unique ownership model and strict borrowing rules. This can result in a considerable investment of time and resources in retraining existing teams or hiring new ones proficient in Rust.&lt;/p>
&lt;h2 id="mature-ecosystem">Mature Ecosystem&lt;/h2>
&lt;p>C++ has a well-established and extensive ecosystem of libraries, frameworks, and tools that have evolved over decades. In contrast, Rust&amp;rsquo;s ecosystem is still relatively young and may not offer the same breadth and depth of resources. For specialised domains or legacy systems, finding equivalent Rust libraries or tools can be a daunting task, potentially leading to project delays or compromises in functionality.&lt;/p>
&lt;h2 id="performance-trade-offs">Performance Trade-offs&lt;/h2>
&lt;p>While Rust&amp;rsquo;s emphasis on safety is commendable, it comes at a cost. The strict ownership model and borrow checker can introduce overhead, impacting runtime performance compared to C++. In scenarios where raw performance is paramount, such as real-time systems or high-frequency trading applications, C++ may still hold a distinct advantage.&lt;/p>
&lt;h2 id="lack-of-legacy-code-compatibility">Lack of Legacy Code Compatibility&lt;/h2>
&lt;p>For projects with existing codebases in C++ or interdependent legacy systems, migrating to Rust might not be a straightforward process. Rewriting or retrofitting existing code to work with Rust&amp;rsquo;s ownership model can be a substantial undertaking, potentially outweighing the benefits gained from Rust&amp;rsquo;s safety guarantees.&lt;/p>
&lt;h2 id="resource-constraints">Resource Constraints&lt;/h2>
&lt;p>In certain resource-constrained environments, such as embedded systems or IoT devices with limited memory and processing power, Rust&amp;rsquo;s runtime and memory management system may not be the most efficient choice. C++ provides more fine-grained control over memory allocation and deallocation, allowing for optimization in resource-constrained environments.&lt;/p>
&lt;h2 id="industry-adoption-and-support">Industry Adoption and Support&lt;/h2>
&lt;p>C++ has been a cornerstone of system-level programming for decades and is deeply entrenched in many critical industries, including aerospace, gaming, finance, and telecommunications. The vast majority of existing codebases in these sectors are written in C++. The risk and cost associated with a wholesale transition to Rust, both in terms of retraining personnel and rewriting existing code, may not be justified by the perceived benefits.&lt;/p>
&lt;p>In conclusion, while Rust brings significant advancements in safety and memory management to the table, it is not a one-size-fits-all replacement for C++. It&amp;rsquo;s crucial to assess the specific needs and constraints of a project before making a decision. In some cases, the benefits of adopting Rust may be outweighed by the challenges and costs associated with the transition. Ultimately, the choice between C++ and Rust should be driven by a thorough evaluation of the project&amp;rsquo;s requirements and the strengths of each language.&lt;/p></description></item><item><title>Unraveling the Causes of Burnout in Software Developers and Strategies for Prevention</title><link>https://www.darrenhorrocks.co.uk/unraveling-causes-burnout-software-developers-strategies-prevention/</link><pubDate>Fri, 29 Sep 2023 08:45:04 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/unraveling-causes-burnout-software-developers-strategies-prevention/</guid><description>&lt;p>In the fast-paced world of software development, burnout has become an alarming concern. Long hours, high pressure, and relentless deadlines can take a toll on even the most dedicated professionals. In this article, we&amp;rsquo;ll explore the root causes of burnout in software developers and provide actionable strategies to help prevent it.&lt;/p>
&lt;h2 id="understanding-burnout">Understanding Burnout&lt;/h2>
&lt;p>Burnout is a state of chronic physical and emotional exhaustion, often accompanied by cynicism and detachment from work. It&amp;rsquo;s a result of prolonged exposure to stressors, which can lead to feelings of hopelessness and reduced efficiency.&lt;/p>
&lt;h2 id="causes-of-burnout-in-software-developers">Causes of Burnout in Software Developers&lt;/h2>
&lt;h3 id="excessive-workload">Excessive Workload&lt;/h3>
&lt;p>One of the primary contributors to burnout is an overwhelming workload. Developers often face tight project deadlines and an ever-expanding list of tasks, leaving little room for breaks or personal time.&lt;/p>
&lt;h3 id="lack-of-autonomy">Lack of Autonomy&lt;/h3>
&lt;p>When developers are micromanaged or have little say in the decision-making process, it can lead to a sense of frustration and helplessness. Autonomy is crucial for job satisfaction and a sense of ownership in one&amp;rsquo;s work.&lt;/p>
&lt;h3 id="unclear-expectations">Unclear Expectations&lt;/h3>
&lt;p>Unclear or constantly changing project goals, requirements, or expectations can create a sense of chaos and instability, leading to stress and burnout.&lt;/p>
&lt;h3 id="inadequate-resources">Inadequate Resources&lt;/h3>
&lt;p>Insufficient tools, outdated technology, or a lack of necessary resources can hinder a developer&amp;rsquo;s ability to perform their job effectively. This can lead to increased frustration and a diminished sense of accomplishment.&lt;/p>
&lt;h3 id="perpetual-crunch-time">Perpetual Crunch Time&lt;/h3>
&lt;p>Frequent periods of &amp;ldquo;crunch time&amp;rdquo; or sustained overtime can be detrimental to both physical and mental well-being. While short bursts of intense work may be necessary, prolonged periods can lead to burnout.&lt;/p>
&lt;h3 id="lack-of-recognition">Lack of Recognition&lt;/h3>
&lt;p>A lack of acknowledgment or appreciation for a developer&amp;rsquo;s hard work can lead to feelings of undervaluation, which can contribute to burnout.&lt;/p>
&lt;h3 id="isolation">Isolation&lt;/h3>
&lt;p>Working in isolation, whether due to remote work arrangements or an isolated workspace, can lead to feelings of loneliness and detachment, which can exacerbate burnout.&lt;/p>
&lt;h2 id="strategies-to-prevent-burnout">Strategies to Prevent Burnout&lt;/h2>
&lt;h3 id="set-realistic-expectations">Set Realistic Expectations&lt;/h3>
&lt;p>Clear, achievable goals and deadlines are essential. Avoid overloading developers with an unrealistic amount of work.&lt;/p>
&lt;h3 id="foster-autonomy">Foster Autonomy&lt;/h3>
&lt;p>Encourage decision-making and provide opportunities for developers to have a say in the projects they work on. This fosters a sense of ownership and responsibility.&lt;/p>
&lt;h3 id="provide-adequate-resources">Provide Adequate Resources&lt;/h3>
&lt;p>Ensure developers have access to the necessary tools, technology, and training to perform their jobs effectively. Up-to-date resources can streamline workflows and increase efficiency.&lt;/p>
&lt;h3 id="promote-work-life-balance">Promote Work-Life Balance&lt;/h3>
&lt;p>Encourage regular breaks and time off. Discourage excessive overtime and ensure that employees have time to rest and recharge.&lt;/p>
&lt;h3 id="recognize-and-celebrate-achievements">Recognize and Celebrate Achievements&lt;/h3>
&lt;p>Acknowledge and celebrate the achievements and contributions of developers. Recognition can boost morale and motivate continued high-quality work.&lt;/p>
&lt;h3 id="foster-a-supportive-work-environment">Foster a Supportive Work Environment&lt;/h3>
&lt;p>Encourage open communication, collaboration, and camaraderie among team members. This can help combat feelings of isolation and promote a positive work culture.&lt;/p>
&lt;h3 id="provide-opportunities-for-skill-development">Provide Opportunities for Skill Development&lt;/h3>
&lt;p>Invest in the professional growth of developers by offering opportunities for training, conferences, and skill-building activities. This not only enhances their capabilities but also demonstrates a commitment to their success.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Preventing burnout in software developers requires a proactive and holistic approach. By addressing the root causes and implementing strategies to support the well-being and professional growth of developers, organizations can create a more sustainable and fulfilling work environment. Remember, a healthy and motivated team is key to achieving long-term success in the ever-evolving field of software development.&lt;/p></description></item><item><title>Embracing Efficiency: Shorter Work Hours for Software Developers (And everyone else)</title><link>https://www.darrenhorrocks.co.uk/embracing-efficiency-shorter-work-hours-software-developers/</link><pubDate>Thu, 28 Sep 2023 09:24:41 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/embracing-efficiency-shorter-work-hours-software-developers/</guid><description>&lt;p>In todays world, productivity is the name of the game, especially in the tech industry where software developers are the driving force behind innovation. However, the idea that longer hours lead to greater productivity is no longer considered to be the case by most. Recent research and real-world examples suggest that cutting back on work hours can actually boost productivity for software developers. In this article, we&amp;rsquo;ll explore the reasons behind this shift and the benefits it brings to both developers and their employers.&lt;/p>
&lt;h2 id="the-law-of-diminishing-returns">The Law of Diminishing Returns&lt;/h2>
&lt;p>Think of it as the law of productivity. Beyond a certain point, putting in extra hours doesn&amp;rsquo;t necessarily translate to a proportional increase in output. In fact, it can lead to burnout and a dip in the quality of work. By opting for shorter workdays, developers can maintain a higher level of focus and creativity throughout their work hours, resulting in more productive output.&lt;/p>
&lt;h2 id="parkinsons-law-work-expands-to-fill-the-time-available">Parkinson&amp;rsquo;s Law: Work Expands to Fill the Time Available&lt;/h2>
&lt;p>According to Parkinson&amp;rsquo;s Law, work tends to expand to fit the time allotted for its completion. This means if developers have more time than they actually need for a task, efficiency might take a hit. Shorter work hours create a sense of urgency, encouraging developers to work more efficiently within the allotted time.&lt;/p>
&lt;h2 id="balancing-work-and-life">Balancing Work and Life&lt;/h2>
&lt;p>A balanced work-life equation is essential for sustained productivity. When employees are overworked, it leads to increased stress, lower morale, and a higher chance of turnover. Offering shorter work hours shows a commitment to employees' well-being, creating a positive work environment, reducing turnover, and attracting top talent.&lt;/p>
&lt;h2 id="sharper-focus-and-more-creativity">Sharper Focus and More Creativity&lt;/h2>
&lt;p>Long working hours can lead to mental fatigue, which can stifle creativity and problem-solving abilities. A reduction in working hours lets developers approach tasks with renewed focus and a fresh perspective, resulting in more innovative solutions and quicker development cycles.&lt;/p>
&lt;h2 id="optimised-time-management">Optimised Time Management&lt;/h2>
&lt;p>Shorter work hours encourage better time management. Knowing they have limited hours encourages developers to prioritise tasks and approach assignments with more determination. This often leads to a more organised and streamlined workflow.&lt;/p>
&lt;h2 id="prioritizing-health-and-well-being">Prioritizing Health and Well-being&lt;/h2>
&lt;p>Physical and mental well-being are essential for sustained productivity. Longer working hours can lead to health issues, both physical and psychological. By reducing working hours, employers contribute to the overall health and well-being of their workforce. Healthy, well-rested employees are more likely to perform optimally, leading to fewer sick days and increased job satisfaction.&lt;/p>
&lt;h2 id="continuous-growth-and-learning">Continuous Growth and Learning&lt;/h2>
&lt;p>In the ever-evolving tech industry, ongoing learning and skill development are vital. Fewer working hours give developers more time for personal and professional growth. This might involve pursuing additional certifications, attending workshops, or engaging in side projects. It&amp;rsquo;s a win-win, benefiting both the developer and the company by keeping skills up-to-date and relevant.&lt;/p>
&lt;h2 id="boosted-motivation-and-engagement">Boosted Motivation and Engagement&lt;/h2>
&lt;p>Knowing they have a more manageable workload, developers are likely to approach their tasks with increased motivation and enthusiasm. This often leads to higher levels of engagement with their work, resulting in higher-quality output.&lt;/p>
&lt;h1 id="advocating-for-shorter-work-hours">Advocating for Shorter Work Hours&lt;/h1>
&lt;p>As employees, it&amp;rsquo;s essential to recognise the benefits of shorter work hours. If you believe this approach could enhance productivity in your workplace, consider initiating a conversation with management. Presenting the evidence and making a case for more efficient work hours can lead to a more productive and fulfilling work environment for everyone involved. Remember, it&amp;rsquo;s a step towards a healthier, more balanced, and ultimately more successful work culture.&lt;/p></description></item><item><title>Mental Health in Tech: Prioritizing Employee Wellbeing for Enhanced Productivity</title><link>https://www.darrenhorrocks.co.uk/mental-health-in-tech/</link><pubDate>Wed, 27 Sep 2023 08:00:13 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/mental-health-in-tech/</guid><description>&lt;p>In the fast-paced world of technology, where lines of code and lines of communication intersect, there&amp;rsquo;s a silent but significant force driving innovation – the mental wellbeing of tech professionals. Balancing the demands of a constantly evolving industry with personal wellness can be a formidable challenge. Yet, it&amp;rsquo;s a challenge that must be met head-on for the sake of both employees and the companies they serve.&lt;/p>
&lt;h2 id="a-balancing-act">A Balancing Act&lt;/h2>
&lt;p>In the realm of technology, the pressure to stay ahead of the curve is relentless. New languages, frameworks, and methodologies emerge almost as quickly as the latest meme on social media. In this environment, it&amp;rsquo;s easy to overlook the crucial role that mental health plays in sustaining a thriving workforce.&lt;/p>
&lt;p>Tech professionals are no strangers to the pressures of tight deadlines, demanding clients, and the pursuit of perfection. These stressors, when left unchecked, can lead to burnout and a decline in overall productivity. Recognizing the signs of stress and providing resources for self-care is not just a nice gesture; it&amp;rsquo;s an investment in the longevity and vitality of your team.&lt;/p>
&lt;h2 id="breaking-the-stigma">Breaking the Stigma&lt;/h2>
&lt;p>In the tech world, there&amp;rsquo;s often an unspoken expectation of perpetual productivity. Long hours, late-night coding sessions, and constant connectivity can become the norm. However, it&amp;rsquo;s crucial to challenge this mindset and create an environment where mental health is openly acknowledged and prioritized.&lt;/p>
&lt;p>Encouraging open conversations about mental health is the first step. When employees feel safe discussing their struggles, it fosters a culture of empathy and support. Simple gestures like regular check-ins, wellness programs, and access to mental health resources can make a world of difference.&lt;/p>
&lt;h2 id="the-ripple-effect">The Ripple Effect&lt;/h2>
&lt;p>Prioritizing mental health isn&amp;rsquo;t just about individual wellbeing; it&amp;rsquo;s about the collective strength of a team. When employees feel supported and valued, they&amp;rsquo;re more likely to be engaged and motivated. This leads to increased creativity, higher job satisfaction, and ultimately, enhanced productivity.&lt;/p>
&lt;p>Furthermore, a mentally healthy workforce can serve as a powerful recruitment tool. Top talent is drawn to companies that demonstrate a genuine commitment to the wellbeing of their employees. By embracing mental health as a cornerstone of company culture, tech firms can attract and retain the best and brightest in the industry.&lt;/p>
&lt;h2 id="embracing-flexibility">Embracing Flexibility&lt;/h2>
&lt;p>In a world where remote work has become the new norm, it&amp;rsquo;s essential to recognize the benefits of flexibility for mental health. Allowing employees to structure their workdays in a way that aligns with their individual needs can lead to a more balanced and fulfilled workforce.&lt;/p>
&lt;p>Flexibility also extends to providing opportunities for skill-building and career growth. Investing in personal development not only enhances employees' confidence and job satisfaction but also contributes to a positive and forward-thinking work environment.&lt;/p>
&lt;h2 id="the-future-of-tech-and-wellbeing">The Future of Tech and Wellbeing&lt;/h2>
&lt;p>As technology continues to revolutionize industries and reshape the way we live and work, it&amp;rsquo;s imperative that we don&amp;rsquo;t lose sight of our most valuable resource – the people behind the screens. Prioritizing mental health in tech isn&amp;rsquo;t just a feel-good initiative; it&amp;rsquo;s a strategic imperative that can drive innovation, foster resilience, and ultimately lead to greater success.&lt;/p>
&lt;p>In this dynamic landscape, let&amp;rsquo;s remember that the wellbeing of our employees is not a checkbox on a to-do list, but a foundation upon which a thriving tech ecosystem can be built. By nurturing the mental health of our teams, we&amp;rsquo;re not only investing in their future but also in the future of technology itself. Together, we can forge a path that leads to innovation, productivity, and a more compassionate tech industry for all.&lt;/p></description></item><item><title>Enhancing Unity Game Performance with Universal Render Pipeline (URP)</title><link>https://www.darrenhorrocks.co.uk/enhancing-unity-game-performance-with-universal-render-pipeline-urp/</link><pubDate>Tue, 26 Sep 2023 10:35:44 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/enhancing-unity-game-performance-with-universal-render-pipeline-urp/</guid><description>&lt;p>The Universal Render Pipeline (URP) is a powerful tool that can significantly improve the performance of your Unity game. By leveraging its features and implementing optimisation techniques, you can create a smoother and more responsive gaming experience. In this article, we&amp;rsquo;ll explore key strategies for performance tuning in Unity games using the URP pipeline. When I started using unity, I had no idea what it was that I was missing, so to help you get started, here are a list of things any Unity developer should know about, and look up.&lt;/p>
&lt;h2 id="utilise-gpu-instancing">Utilise GPU Instancing&lt;/h2>
&lt;p>GPU instancing allows multiple objects with the same material to be rendered in a single draw call. This reduces the overhead associated with making separate draw calls for each object.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">void&lt;/span> Start()
{
Renderer renderer = GetComponent&amp;lt;Renderer&amp;gt;();
&lt;span style="color:#66d9ef">if&lt;/span> (renderer != &lt;span style="color:#66d9ef">null&lt;/span>)
{
renderer.material.enableInstancing = &lt;span style="color:#66d9ef">true&lt;/span>;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="optimise-shaders">Optimise Shaders&lt;/h2>
&lt;p>URP offers a Shader Graph that allows for visually designing shaders. Keep these tips in mind:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Simplify Shaders:&lt;/strong> Avoid complex operations in shaders, especially within fragment shaders.&lt;/li>
&lt;li>&lt;strong>Use LODs for Shaders:&lt;/strong> Employ Level of Detail techniques for shaders to reduce complexity for distant objects.&lt;/li>
&lt;/ul>
&lt;h2 id="texture-compression-and-atlasing">Texture Compression and Atlasing&lt;/h2>
&lt;p>Optimise textures to reduce memory usage and enhance rendering performance:&lt;/p>
&lt;ul>
&lt;li>Use compressed texture formats like &lt;strong>ASTC&lt;/strong>, &lt;strong>BC7&lt;/strong>, or &lt;strong>ETC2&lt;/strong>.&lt;/li>
&lt;li>Combine multiple textures into atlases to reduce draw calls
&lt;ul>
&lt;li>&lt;strong>Generate or Collect Textures:&lt;/strong> Gather the individual textures you want to include in the atlas.&lt;/li>
&lt;li>&lt;strong>Create Atlas Texture:&lt;/strong> Use a graphics editing tool (e.g., Photoshop) to create a new image file, which will serve as the atlas.&lt;/li>
&lt;li>&lt;strong>Arrange Textures:&lt;/strong> Place the individual textures within the atlas, organizing them to minimize wasted space.&lt;/li>
&lt;li>&lt;strong>Import into Unity:&lt;/strong> Import the atlas texture into your Unity project.&lt;/li>
&lt;li>&lt;strong>Apply Atlas to Materials:&lt;/strong> Modify materials to use the atlas texture instead of individual textures.&lt;/li>
&lt;li>&lt;strong>Adjust UV Mapping:&lt;/strong> Ensure that UV mapping for the objects corresponds correctly to the atlas.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="implement-occlusion-culling">Implement Occlusion Culling&lt;/h2>
&lt;p>Occlusion culling prevents the rendering of objects that are not visible to the camera. This reduces GPU workload.&lt;/p>
&lt;h2 id="level-of-detail-lod">Level of Detail (LOD)&lt;/h2>
&lt;p>LOD techniques involve using lower-polygon models or simplified textures for distant objects. URP provides tools for LOD implementation.&lt;/p>
&lt;h3 id="prepare-lod-models">Prepare LOD Models&lt;/h3>
&lt;ul>
&lt;li>Create simplified versions of your object with fewer polygons for varying distances.&lt;/li>
&lt;/ul>
&lt;h3 id="add-lod-group">Add LOD Group&lt;/h3>
&lt;ul>
&lt;li>Select the object in the Hierarchy.&lt;/li>
&lt;li>Add the LOD Group component.&lt;/li>
&lt;/ul>
&lt;h3 id="assign-lod-levels">Assign LOD Levels&lt;/h3>
&lt;ul>
&lt;li>In the LOD Group, add LOD levels and assign corresponding models.&lt;/li>
&lt;li>Set Screen Relative Transition Height for each level.&lt;/li>
&lt;/ul>
&lt;h3 id="test-and-adjust">Test and Adjust&lt;/h3>
&lt;ul>
&lt;li>Enter Play mode to observe LOD switching.&lt;/li>
&lt;li>Fine-tune transition distances if needed.&lt;/li>
&lt;/ul>
&lt;h3 id="optimise-lod-models">Optimise LOD Models&lt;/h3>
&lt;ul>
&lt;li>Ensure LOD models are well-optimised for performance.&lt;/li>
&lt;/ul>
&lt;h3 id="verify-performance">Verify Performance&lt;/h3>
&lt;ul>
&lt;li>Test on different devices to ensure desired performance improvements.&lt;/li>
&lt;/ul>
&lt;p>LOD is effective for objects like terrain, foliage, and complex structures. Apply it to objects with a significant impact on performance.&lt;/p>
&lt;h2 id="particle-system-optimisation">Particle System Optimisation&lt;/h2>
&lt;p>Efficiently managing particle systems is crucial for performance:&lt;/p>
&lt;ul>
&lt;li>Limit the number of particles emitted at a time.&lt;/li>
&lt;li>Use GPU simulation if suitable for your project.&lt;/li>
&lt;/ul>
&lt;h2 id="minimise-overdraw">Minimise Overdraw&lt;/h2>
&lt;p>Overdraw occurs when multiple transparent objects are rendered on top of each other. This can be mitigated with techniques such as:&lt;/p>
&lt;ul>
&lt;li>Sorting objects by depth to minimise overdraw.&lt;/li>
&lt;li>Utilizing techniques like depth pre-pass.&lt;/li>
&lt;/ul>
&lt;h2 id="optimise-post-processing-effects">Optimise Post-Processing Effects&lt;/h2>
&lt;p>Post-processing effects can be resource-intensive. Consider:&lt;/p>
&lt;ul>
&lt;li>Disabling or reducing the intensity of non-essential effects.&lt;/li>
&lt;li>Using URP&amp;rsquo;s built-in optimisations for post-processing.&lt;/li>
&lt;/ul>
&lt;h2 id="gpu-driven-rendering">GPU-Driven Rendering&lt;/h2>
&lt;p>URP leverages GPU for various rendering tasks. Maximize its potential by utilizing Compute Shaders and other GPU-driven techniques.&lt;/p>
&lt;h2 id="test-on-target-hardware">Test on Target Hardware&lt;/h2>
&lt;p>Always test your game on the actual target hardware to ensure optimal performance. This allows you to uncover platform-specific issues and fine-tune accordingly.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Leveraging the Universal Render Pipeline in Unity provides a powerful set of tools for optimising game performance. By utilizing GPU instancing, optimising shaders, managing textures efficiently, and implementing various other techniques, you can create a high-performing game across a range of devices. Remember, continuous testing and iteration are crucial for achieving the best possible performance.&lt;/p></description></item><item><title>Feedback Culture: Constructive Criticism vs. Destructive Blame in Software Companies</title><link>https://www.darrenhorrocks.co.uk/constructive-criticism-vs-destructive-blame-in-software-companies/</link><pubDate>Mon, 25 Sep 2023 08:10:43 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/constructive-criticism-vs-destructive-blame-in-software-companies/</guid><description>&lt;p>In the fast-paced and dynamic world of software development, an effective feedback culture is paramount for growth, innovation, and overall success. However, not all feedback is created equal. It is crucial to distinguish between constructive criticism and destructive blame, as they can have vastly different impacts on teams and individuals within a software company.&lt;/p>
&lt;h2 id="the-importance-of-constructive-criticism">The Importance of Constructive Criticism&lt;/h2>
&lt;p>Constructive criticism is a cornerstone of a healthy feedback culture within a software company. It is a process of providing thoughtful, specific, and actionable feedback aimed at helping individuals improve their skills, work, and contributions. Here&amp;rsquo;s why constructive criticism is essential:&lt;/p>
&lt;h3 id="fosters-continuous-improvement">Fosters Continuous Improvement&lt;/h3>
&lt;p>Constructive criticism is geared towards improvement. It pinpoints areas of strength and weakness, offering valuable insights for growth. In a software development context, this can lead to more efficient coding practices, improved problem-solving, and ultimately, better software products.&lt;/p>
&lt;h3 id="encourages-open-communication">Encourages Open Communication&lt;/h3>
&lt;p>A culture of constructive criticism promotes open dialogue among team members. It creates an environment where everyone feels comfortable sharing ideas, seeking clarification, and offering feedback in return. This transparent communication is vital for innovation and problem-solving.&lt;/p>
&lt;h3 id="builds-trust-and-respect">Builds Trust and Respect&lt;/h3>
&lt;p>When feedback is delivered in a constructive manner, it shows respect for the individual&amp;rsquo;s efforts and skills. It acknowledges their potential and fosters trust between team members and management. Trust is crucial for a collaborative and productive work environment.&lt;/p>
&lt;h3 id="nurtures-professional-development">Nurtures Professional Development&lt;/h3>
&lt;p>Constructive criticism is an investment in the professional growth of team members. It helps them identify areas where they can upskill and expand their knowledge. This not only benefits the individual but also contributes to the overall competence of the team.&lt;/p>
&lt;h2 id="the-pitfalls-of-destructive-blame">The Pitfalls of Destructive Blame&lt;/h2>
&lt;p>Destructive blame, on the other hand, can be detrimental to the morale, productivity, and ultimately, the success of a software company. It involves assigning fault without offering any meaningful guidance or solutions. Here&amp;rsquo;s why it should be avoided:&lt;/p>
&lt;h3 id="creates-a-hostile-work-environment">Creates a Hostile Work Environment&lt;/h3>
&lt;p>Blame creates a toxic atmosphere where team members feel hesitant to take risks or share their ideas. It erodes trust and stifles creativity, hindering the potential for innovation and progress.&lt;/p>
&lt;h3 id="hinders-problem-solving">Hinders Problem-Solving&lt;/h3>
&lt;p>Blaming does not address the root cause of a problem. Instead, it places blame on individuals or teams, diverting attention from finding effective solutions. This can lead to persistent issues and a lack of progress.&lt;/p>
&lt;h3 id="undermines-employee-morale">Undermines Employee Morale&lt;/h3>
&lt;p>Constant blame erodes confidence and motivation. It can lead to disengagement, absenteeism, and, ultimately, a higher turnover rate. A demoralized team is less likely to be productive or deliver high-quality work.&lt;/p>
&lt;h3 id="stifles-growth-and-learning">Stifles Growth and Learning&lt;/h3>
&lt;p>Blame discourages individuals from taking risks or trying new approaches. Without the opportunity to learn from mistakes, growth and innovation are stifled, potentially leading to stagnation in a rapidly evolving field like software development.&lt;/p>
&lt;h2 id="striking-a-balance">Striking a Balance&lt;/h2>
&lt;p>To cultivate a healthy feedback culture, software companies must strike a balance between constructive criticism and destructive blame. This involves:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Clear Communication:&lt;/strong> Encourage open and honest communication channels within the team, where feedback is welcomed and valued.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Focus on Solutions:&lt;/strong> When issues arise, emphasize finding solutions rather than assigning blame. This promotes a problem-solving mindset.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Training and Development:&lt;/strong> Provide resources and opportunities for professional development, ensuring team members have the tools they need to excel.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Lead by Example:&lt;/strong> Managers and leaders should model effective feedback practices, setting the tone for the entire team.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>In conclusion, a robust feedback culture is the cornerstone of success in software companies. Recognizing the distinction between constructive criticism and destructive blame is crucial for fostering an environment of growth, collaboration, and innovation. By prioritizing constructive feedback, software companies can unlock their full potential and thrive in an ever-evolving industry.&lt;/p></description></item><item><title>What is the difference betwen Promises and Observables in angular?</title><link>https://www.darrenhorrocks.co.uk/what-difference-betwen-promises-observables-angular/</link><pubDate>Sun, 24 Sep 2023 08:52:45 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/what-difference-betwen-promises-observables-angular/</guid><description>&lt;p>In Angular, Promises and Observables are both used for handling asynchronous operations, but they have some key differences in how they work and what they offer. Here are the main distinctions between the two:&lt;/p>
&lt;h2 id="primises">Primises&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Single Value:&lt;/strong> A Promise represents a single future value. It&amp;rsquo;s a proxy for a value not necessarily known when the promise is created.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Eager:&lt;/strong> Promises are eager, meaning that they immediately start executing when they are created.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Not Cancellable:&lt;/strong> Once a promise is created, it cannot be cancelled.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>One-time:&lt;/strong> A Promise can only emit a single value, either a resolved value or a rejected reason.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>.then() syntax:&lt;/strong> Promises are typically handled using &lt;code>.then()&lt;/code> and &lt;code>.catch()&lt;/code>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="color:#a6e22e">someAsyncFunction&lt;/span>()
.&lt;span style="color:#a6e22e">then&lt;/span>(&lt;span style="color:#a6e22e">result&lt;/span> =&amp;gt; {
&lt;span style="color:#75715e">// Do something with the result
&lt;/span>&lt;span style="color:#75715e">&lt;/span> })
.&lt;span style="color:#66d9ef">catch&lt;/span>(&lt;span style="color:#a6e22e">error&lt;/span> =&amp;gt; {
&lt;span style="color:#75715e">// Handle errors
&lt;/span>&lt;span style="color:#75715e">&lt;/span> });
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="observables">Observables&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Multiple Values:&lt;/strong> An Observable is a stream of values that may be emitted over time. It can emit zero or more values.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Lazy:&lt;/strong> Observables are lazy, meaning they do not start emitting values until a subscription is made.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cancellable:&lt;/strong> You can cancel an observable by unsubscribing.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Continuous:&lt;/strong> An Observable can keep emitting values over time, which makes it suitable for handling events or continuous streams of data.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>.subscribe() syntax:&lt;/strong> Observables are typically handled using &lt;code>.subscribe()&lt;/code>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="color:#a6e22e">someAsyncObservable&lt;/span>()
.&lt;span style="color:#a6e22e">subscribe&lt;/span>(
&lt;span style="color:#a6e22e">value&lt;/span> =&amp;gt; {
&lt;span style="color:#75715e">// Do something with each emitted value
&lt;/span>&lt;span style="color:#75715e">&lt;/span> },
&lt;span style="color:#a6e22e">error&lt;/span> =&amp;gt; {
&lt;span style="color:#75715e">// Handle errors
&lt;/span>&lt;span style="color:#75715e">&lt;/span> },
() =&amp;gt; {
&lt;span style="color:#75715e">// Handle completion (optional)
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
);
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="when-to-use-which">When to Use Which&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Promises&lt;/strong> are suitable for operations that produce a single value. They are a good fit for scenarios where you make a single asynchronous request and expect a single response.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Observables&lt;/strong> are more powerful when dealing with multiple values or streams of data. They are especially useful for handling events, continuous data streams (like from WebSockets), and scenarios where you might need to cancel or transform the data before it reaches the subscriber.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>In Angular, HTTP requests using the HttpClient return Observables by default, which allows you to leverage the full power of Observables for handling the asynchronous nature of HTTP requests.&lt;/p>
&lt;p>In summary, if you&amp;rsquo;re dealing with a single asynchronous operation, a Promise might be sufficient. However, if you&amp;rsquo;re working with streams of data, events, or need more control over cancellation, Observables are the way to&lt;/p></description></item><item><title>Navigating Moral Dilemmas in Software Development</title><link>https://www.darrenhorrocks.co.uk/navigating-moral-dilemmas-in-software-development/</link><pubDate>Sat, 23 Sep 2023 08:00:42 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/navigating-moral-dilemmas-in-software-development/</guid><description>&lt;p>In today&amp;rsquo;s tech-driven world, being a software developer means more than just writing code. It means taking on the responsibility of ensuring that our creations not only work well but also do good. Let&amp;rsquo;s dive into some key ethical considerations for developers:&lt;/p>
&lt;h3 id="respecting-user-privacy">Respecting User Privacy&lt;/h3>
&lt;p>Think of user data like a trust fall - they&amp;rsquo;re putting their information in your hands. Be clear about what you&amp;rsquo;re collecting and why. Always ask for permission before diving into someone&amp;rsquo;s personal details.&lt;/p>
&lt;h3 id="building-fortresses-security-first">Building Fortresses: Security First&lt;/h3>
&lt;p>Just like a well-locked door keeps your home safe, a secure software protects users. Don&amp;rsquo;t skimp on security measures. Regular updates and staying ahead of potential threats are your keys to success.&lt;/p>
&lt;h3 id="bias-busting">Bias Busting&lt;/h3>
&lt;p>Nobody likes playing favorites. Be vigilant against biases creeping into your algorithms. Ensure your software treats everyone fairly, regardless of who they are.&lt;/p>
&lt;h3 id="inclusive-design">Inclusive Design&lt;/h3>
&lt;p>Imagine building a park with only one entrance - not very inviting, right? Make your software accessible to all, regardless of abilities. Think of it as creating a digital space that everyone can comfortably stroll through.&lt;/p>
&lt;h3 id="transparent-and-accountable">Transparent and Accountable&lt;/h3>
&lt;p>No one likes secrets, especially in tech. Be upfront about how your software works. And if something goes awry, take responsibility and make it right.&lt;/p>
&lt;h2 id="navigating-ethical-dilemmas">Navigating Ethical Dilemmas&lt;/h2>
&lt;p>In the whirlwind of development, ethics can sometimes take a back seat. Here&amp;rsquo;s how to keep it front and center:&lt;/p>
&lt;h3 id="stay-informed">Stay Informed&lt;/h3>
&lt;p>Keep an eye on the latest ethical discussions in tech. Knowing what&amp;rsquo;s up in the field helps you make better decisions.&lt;/p>
&lt;h3 id="chat-it-out">Chat it Out&lt;/h3>
&lt;p>Have open conversations with your team about the ethics of your projects. Sometimes a fresh perspective is just what you need.&lt;/p>
&lt;h3 id="follow-ethical-blueprints">Follow Ethical Blueprints&lt;/h3>
&lt;p>Use established ethical guidelines like &amp;ldquo;privacy by design&amp;rdquo;. It&amp;rsquo;s like having a roadmap that leads you in the right direction.&lt;/p>
&lt;h3 id="test-for-ethics">Test for Ethics&lt;/h3>
&lt;p>Give your software a moral check-up. Test for biases, privacy, and anything else that might raise an ethical flag.&lt;/p>
&lt;h3 id="keep-growing">Keep Growing&lt;/h3>
&lt;p>Tech ethics is a bit like a growing plant. It needs care and attention. Stay curious and keep learning about new challenges and solutions.&lt;/p>
&lt;h2 id="in-a-nutshell">In a Nutshell&lt;/h2>
&lt;p>Being a software developer isn&amp;rsquo;t just about writing code; it&amp;rsquo;s about making sure your creations make a positive impact. By weaving ethics into your work, you&amp;rsquo;re not just building better software - you&amp;rsquo;re building a better world.&lt;/p></description></item><item><title>Securing ASP.NET Core Applications Against SQL Injection Attacks with Microsoft SQL Server</title><link>https://www.darrenhorrocks.co.uk/securing-asp-net-core-against-sql-injection-attacks/</link><pubDate>Fri, 22 Sep 2023 21:23:32 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/securing-asp-net-core-against-sql-injection-attacks/</guid><description>&lt;p>SQL injection attacks remain a significant threat to web applications. When using Microsoft SQL Server with ASP.NET Core applications, it&amp;rsquo;s crucial to implement robust security measures to prevent SQL injection vulnerabilities. This article outlines best practices and techniques to avoid SQL injection attacks in ASP.NET Core applications.&lt;/p>
&lt;h2 id="parameterized-queries-and-stored-procedures">Parameterized Queries and Stored Procedures&lt;/h2>
&lt;h3 id="parameterized-queries">Parameterized Queries&lt;/h3>
&lt;p>Parameterized queries are a fundamental defense against SQL injection attacks. They allow you to separate SQL code from user input, making it extremely difficult for an attacker to inject malicious SQL code.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">string&lt;/span> query = &lt;span style="color:#e6db74">&amp;#34;SELECT * FROM Users WHERE Username = @Username AND Password = @Password&amp;#34;&lt;/span>;
SqlCommand command = &lt;span style="color:#66d9ef">new&lt;/span> SqlCommand(query, connection);
command.Parameters.AddWithValue(&lt;span style="color:#e6db74">&amp;#34;@Username&amp;#34;&lt;/span>, username);
command.Parameters.AddWithValue(&lt;span style="color:#e6db74">&amp;#34;@Password&amp;#34;&lt;/span>, password);
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="stored-procedures">Stored Procedures&lt;/h3>
&lt;p>Stored procedures can further enhance security by encapsulating SQL logic on the server side. They prevent direct manipulation of the database by the user.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">PROCEDURE&lt;/span> dbo.ValidateUser
&lt;span style="color:#f92672">@&lt;/span>Username NVARCHAR(&lt;span style="color:#ae81ff">50&lt;/span>),
&lt;span style="color:#f92672">@&lt;/span>Password NVARCHAR(&lt;span style="color:#ae81ff">50&lt;/span>)
&lt;span style="color:#66d9ef">AS&lt;/span>
&lt;span style="color:#66d9ef">BEGIN&lt;/span>
&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> Users &lt;span style="color:#66d9ef">WHERE&lt;/span> Username &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">@&lt;/span>Username &lt;span style="color:#66d9ef">AND&lt;/span> Password &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#f92672">@&lt;/span>Password
&lt;span style="color:#66d9ef">END&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">string&lt;/span> procedureName = &lt;span style="color:#e6db74">&amp;#34;ValidateUser&amp;#34;&lt;/span>;
SqlCommand command = &lt;span style="color:#66d9ef">new&lt;/span> SqlCommand(procedureName, connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue(&lt;span style="color:#e6db74">&amp;#34;@Username&amp;#34;&lt;/span>, username);
command.Parameters.AddWithValue(&lt;span style="color:#e6db74">&amp;#34;@Password&amp;#34;&lt;/span>, password);
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="use-object-relational-mapping-orm">Use Object-Relational Mapping (ORM)&lt;/h2>
&lt;p>Using an ORM framework like Entity Framework Core can help mitigate SQL injection risks. ORM frameworks generate parameterized queries automatically, reducing the likelihood of introducing vulnerabilities through manual query construction.&lt;/p>
&lt;h2 id="input-validation-and-sanitization">Input Validation and Sanitization&lt;/h2>
&lt;p>Validate and sanitize user input thoroughly. Apply strict validation rules to ensure data adheres to expected formats and ranges. Utilize validation libraries and features provided by ASP.NET Core.&lt;/p>
&lt;h2 id="principle-of-least-privilege">Principle of Least Privilege&lt;/h2>
&lt;p>When using Microsoft SQL Server with ASP.NET Core, it&amp;rsquo;s important to ensure that the SQL Server user account used by your application has the minimum required privileges. This principle limits the potential damage that can be caused if a SQL injection attack were to succeed.&lt;/p>
&lt;p>For example, if your application only needs to perform SELECT operations on a specific table, create a user account with only those permissions:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">CREATE&lt;/span> LOGIN AppUser &lt;span style="color:#66d9ef">WITH&lt;/span> PASSWORD &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;StrongPassword123&amp;#39;&lt;/span>;
&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">USER&lt;/span> AppUser &lt;span style="color:#66d9ef">FOR&lt;/span> LOGIN AppUser;
&lt;span style="color:#66d9ef">GRANT&lt;/span> &lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#66d9ef">ON&lt;/span> YourTable &lt;span style="color:#66d9ef">TO&lt;/span> AppUser;
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="error-handling-and-logging">Error Handling and Logging&lt;/h2>
&lt;p>Implementing detailed error handling and logging is essential for identifying and responding to potential attacks. Avoid exposing sensitive error messages to end-users, as they can provide valuable information to attackers.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#75715e">// Code that interacts with the database
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;span style="color:#66d9ef">catch&lt;/span> (Exception ex)
{
&lt;span style="color:#75715e">// Log the error
&lt;/span>&lt;span style="color:#75715e">&lt;/span> logger.LogError(ex, &lt;span style="color:#e6db74">&amp;#34;An error occurred while processing a database request.&amp;#34;&lt;/span>);
&lt;span style="color:#75715e">// Display a generic error message to the user
&lt;/span>&lt;span style="color:#75715e">&lt;/span> ViewBag.ErrorMessage = &lt;span style="color:#e6db74">&amp;#34;An error occurred while processing your request. Please try again later.&amp;#34;&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="regular-security-audits-and-penetration-testing">Regular Security Audits and Penetration Testing&lt;/h2>
&lt;p>Regularly conducting security audits and penetration tests can help identify and address potential vulnerabilities, including SQL injection risks. There are various tools and services available for conducting security assessments.&lt;/p>
&lt;p>For example, you can use tools like OWASP ZAP or hire a professional penetration testing service to evaluate your application&amp;rsquo;s security.&lt;/p>
&lt;p>Additionally, consider implementing automated security scanning tools into your development and deployment process to catch potential vulnerabilities early in the development lifecycle.&lt;/p>
&lt;p>By following these best practices and examples, you can significantly enhance the security of your ASP.NET Core application when using Microsoft SQL Server. Remember that security is an ongoing process, and staying vigilant against potential threats is crucial for safeguarding your application and its data.&lt;/p></description></item><item><title>Understanding the Gap between Sales and Software Development in Software Companies</title><link>https://www.darrenhorrocks.co.uk/the-divide-understanding-the-gap-between-sales-and-developers/</link><pubDate>Thu, 21 Sep 2023 09:09:31 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/the-divide-understanding-the-gap-between-sales-and-developers/</guid><description>&lt;p>In the intricate landscape of software companies, a notable divide often emerges between the sales and software development departments. Sales is hailed for its immediate revenue impact, while development is sometimes misconstrued as a cost center. This article aims to dissect the reasons behind this prevalent gap and shed light on the distinct roles that each department plays in a company&amp;rsquo;s success.&lt;/p>
&lt;p>The gap between sales and software development departments in software companies is not necessarily due to &amp;ldquo;bad management&amp;rdquo; universally. While poor management can exacerbate the divide, the root causes of this gap are often more nuanced and can arise from a combination of factors. These may include:&lt;/p>
&lt;h2 id="the-sales-department-catalysts-of-revenue">The Sales Department: Catalysts of Revenue&lt;/h2>
&lt;p>Sales teams are celebrated for their pivotal role in revenue generation. Their tangible metrics, such as deals closed and customer acquisition, provide clear indicators of their performance. This direct impact on the company&amp;rsquo;s top line reinforces their status as revenue catalysts.&lt;/p>
&lt;h2 id="software-development-architects-of-the-future">Software Development: Architects of the Future&lt;/h2>
&lt;p>On the contrary, software development is an investment in the company&amp;rsquo;s future. Their meticulous work underpins product quality and stability. The intricate processes of debugging and refining code, while less immediately visible, are essential for long-term success.&lt;/p>
&lt;h2 id="roots-of-the-divide">Roots of the Divide&lt;/h2>
&lt;p>The divide between sales and software development arises from the distinct nature of their contributions. Sales operates in the realm of immediate, measurable outcomes, while development focuses on long-term product refinement. This discrepancy in visibility and immediacy leads to the perceived gap.&lt;/p>
&lt;h2 id="different-timelines-same-goals">Different Timelines, Same Goals&lt;/h2>
&lt;p>Sales and development operate on different timelines. Sales teams strive for swift results, while developers invest in enduring product excellence. Recognizing the complementary nature of these timelines is crucial in appreciating the value each department brings.&lt;/p>
&lt;h2 id="what-do-we-do-about-it">What Do We Do About It?&lt;/h2>
&lt;p>While management plays a crucial role in mitigating or exacerbating these factors, it&amp;rsquo;s important to recognize that the gap is often a complex interplay of various organizational dynamics. Effective management can certainly help bridge this gap by fostering a culture of collaboration, ensuring transparent communication, and recognizing the value of both departments. However, attributing the entire gap solely to &amp;ldquo;bad management&amp;rdquo; would oversimplify a multifaceted issue.&lt;/p>
&lt;p>In software companies, the divide between sales and development departments stems from the inherent differences in their contributions. While one thrives on immediate, measurable outcomes, the other lays the foundation for long-term success. Understanding the distinctive roles of each department is essential in fostering a culture of mutual appreciation. By recognizing their shared goals and complementary timelines, companies can pave the way for a more harmonious and productive work environment, ultimately leading to sustained growth and success.&lt;/p></description></item><item><title>Working with Entity Framework Core in C#</title><link>https://www.darrenhorrocks.co.uk/working-with-entity-framework-core/</link><pubDate>Thu, 21 Sep 2023 09:00:46 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/working-with-entity-framework-core/</guid><description>&lt;p>Entity Framework Core (EF Core) is a powerful and lightweight Object-Relational Mapping (ORM) framework for .NET applications. It simplifies database operations by allowing developers to work with .NET objects rather than writing raw SQL queries. In this tutorial, we&amp;rsquo;ll explore the fundamentals of using Entity Framework Core in C#.&lt;/p>
&lt;h2 id="getting-started">Getting Started&lt;/h2>
&lt;h3 id="installing-entity-framework-core">Installing Entity Framework Core&lt;/h3>
&lt;p>Before you can start using EF Core, you need to install the necessary packages. In your project, open the NuGet Package Manager and install the &lt;code>Microsoft.EntityFrameworkCore&lt;/code> and &lt;code>Microsoft.EntityFrameworkCore.SqlServer&lt;/code> packages.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="creating-a-dbcontext">Creating a DbContext&lt;/h2>
&lt;p>The &lt;code>DbContext&lt;/code> is the heart of EF Core. It represents a session with the database and allows you to perform operations like querying, inserting, updating, and deleting entities.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">AppDbContext&lt;/span> : DbContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;User&amp;gt; Users { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#75715e">// Add other DbSet properties for your entities here
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(&lt;span style="color:#e6db74">&amp;#34;YourConnectionString&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Replace &lt;code>User&lt;/code> with your own entity and provide your database connection string.&lt;/p>
&lt;h2 id="defining-entities">Defining Entities&lt;/h2>
&lt;p>Entities are C# classes that represent tables in your database. Each property in the class corresponds to a column in the table.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">User&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Email { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="performing-crud-operations">Performing CRUD Operations&lt;/h2>
&lt;h3 id="creating-records">Creating Records&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> AppDbContext())
{
&lt;span style="color:#66d9ef">var&lt;/span> newUser = &lt;span style="color:#66d9ef">new&lt;/span> User { Name = &lt;span style="color:#e6db74">&amp;#34;John Doe&amp;#34;&lt;/span>, Email = &lt;span style="color:#e6db74">&amp;#34;john@example.com&amp;#34;&lt;/span> };
context.Users.Add(newUser);
context.SaveChanges();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="reading-records">Reading Records&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> AppDbContext())
{
&lt;span style="color:#66d9ef">var&lt;/span> users = context.Users.ToList();
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> user &lt;span style="color:#66d9ef">in&lt;/span> users)
{
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;{user.Id}: {user.Name} - {user.Email}&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="updating-records">Updating Records&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> AppDbContext())
{
&lt;span style="color:#66d9ef">var&lt;/span> user = context.Users.FirstOrDefault(u =&amp;gt; u.Id == &lt;span style="color:#ae81ff">1&lt;/span>);
&lt;span style="color:#66d9ef">if&lt;/span> (user != &lt;span style="color:#66d9ef">null&lt;/span>)
{
user.Email = &lt;span style="color:#e6db74">&amp;#34;john.doe@newdomain.com&amp;#34;&lt;/span>;
context.SaveChanges();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="deleting-records">Deleting Records&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> AppDbContext())
{
&lt;span style="color:#66d9ef">var&lt;/span> user = context.Users.FirstOrDefault(u =&amp;gt; u.Id == &lt;span style="color:#ae81ff">1&lt;/span>);
&lt;span style="color:#66d9ef">if&lt;/span> (user != &lt;span style="color:#66d9ef">null&lt;/span>)
{
context.Users.Remove(user);
context.SaveChanges();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="querying-with-linq">Querying with LINQ&lt;/h2>
&lt;p>EF Core allows you to use LINQ (Language Integrated Query) to write expressive and powerful queries.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> AppDbContext())
{
&lt;span style="color:#66d9ef">var&lt;/span> users = context.Users
.Where(u =&amp;gt; u.Name.Contains(&lt;span style="color:#e6db74">&amp;#34;John&amp;#34;&lt;/span>))
.OrderBy(u =&amp;gt; u.Name)
.ToList();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Entity Framework Core provides a seamless way to interact with databases in C# applications. With its powerful features and simplicity, it has become a popular choice for .NET developers. By following this tutorial, you&amp;rsquo;ve gained a solid foundation for working with EF Core, enabling you to build robust and efficient data-driven applications.&lt;/p></description></item><item><title>Building Marlin 2.0.x Firmware for Ender 3 3D Printer and Ender 3 Clones</title><link>https://www.darrenhorrocks.co.uk/building-marlin-firmware-for-ender-3-3d-printer/</link><pubDate>Wed, 20 Sep 2023 15:58:05 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/building-marlin-firmware-for-ender-3-3d-printer/</guid><description>&lt;p>The Marlin firmware is an open-source firmware widely used in 3D printers, known for its flexibility and extensive feature set. This tutorial will guide you through the process of building the Marlin firmware for the Creality Ender 3 using PlatformIO, a powerful open-source ecosystem for IoT development, and Visual Studio Code (VSCode), a popular code editor.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>Before we begin, make sure you have the following:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Visual Studio Code (VSCode)&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Download and install Visual Studio Code.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>PlatformIO Extension&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Open VSCode, go to the Extensions view (Ctrl+Shift+X or Cmd+Shift+X on Mac), and search for &amp;ldquo;PlatformIO IDE&amp;rdquo;. Install it.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Clone Marlin Firmware Repository&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Open a terminal or command prompt and navigate to the directory where you want to store the Marlin firmware.
Run the command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-bash" data-lang="bash">git clone https://github.com/MarlinFirmware/Marlin.git
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="building-marlin-firmware-for-ender-3">Building Marlin Firmware for Ender 3&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Open Project in VSCode&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Open VSCode, and go to &lt;code>File &amp;gt; Open Folder...&lt;/code>, then select the &lt;code>Marlin&lt;/code> folder you just cloned.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Select Board Configuration&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>In the VSCode sidebar, expand the &lt;code>Marlin&lt;/code> folder and navigate to &lt;code>Marlin/Configuration.h&lt;/code>. This file contains the configuration settings for your printer. Look for the section that mentions &lt;code>ENDER_3&lt;/code> or similar. Make sure it&amp;rsquo;s uncommented (remove &lt;code>//&lt;/code> at the beginning of the line) and save the file.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Select Printer Options&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>In the same Configuration.h file, scroll down to customize other settings like bed size, stepper driver type, and more according to your specific Ender 3 model and any modifications you&amp;rsquo;ve made.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Select Environment in PlatformIO&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Click on the PlatformIO icon on the sidebar (looks like a little alien head). Under &amp;ldquo;Project Tasks&amp;rdquo;, expand the &lt;code>env&lt;/code>: dropdown and select &lt;code>STM32F103RET6_creality&lt;/code> for Ender 3.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Build the Firmware&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Click on the &amp;ldquo;PlatformIO: Build&amp;rdquo; icon in the PlatformIO sidebar.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Upload the Firmware&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>If you have a bootloader installed on your Ender 3, you can use a USB cable to upload the firmware. Otherwise, you&amp;rsquo;ll need to use an ISP programmer.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="flashing-firmware-optional">Flashing Firmware (Optional)&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Bootloader Method (Recommended)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>If you have a bootloader, you can use a USB cable to flash the firmware:&lt;/li>
&lt;li>Connect your printer to your computer via USB.&lt;/li>
&lt;li>Click on &amp;ldquo;PlatformIO: Upload&amp;rdquo; in the PlatformIO sidebar.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ISP Programmer Method (If No Bootloader)&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>You&amp;rsquo;ll need an ISP programmer like an AVRISP mkII.&lt;/li>
&lt;li>Connect the ISP programmer to your printer&amp;rsquo;s board.&lt;/li>
&lt;li>In PlatformIO, click on &amp;ldquo;PlatformIO: Upload using Programmer&amp;rdquo; in the sidebar&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="notes">Notes&lt;/h2>
&lt;ul>
&lt;li>Backup Configuration: Before you make any changes, always back up your original &lt;code>Configuration.h&lt;/code> and &lt;code>Configuration_adv.h&lt;/code> files.&lt;/li>
&lt;li>Test the Firmware: After flashing, test the printer to ensure it functions correctly.&lt;/li>
&lt;/ul>
&lt;p>Remember, modifying firmware can potentially damage your printer if done incorrectly. Proceed with caution and double-check your configurations. If you encounter issues, refer to the &lt;a href="https://marlinfw.org/docs/configuration/configuration.html">Marlin Documentation&lt;/a> and community forums for help.&lt;/p></description></item><item><title>When Does Automating Away a Coworkers Job Cross The Line into Bullying</title><link>https://www.darrenhorrocks.co.uk/when-is-automating-away-a-job-considered-bullying/</link><pubDate>Wed, 20 Sep 2023 08:00:10 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/when-is-automating-away-a-job-considered-bullying/</guid><description>&lt;p>In a previous job, I found that a coworker was spending a full work week building an excel spreadsheet from our Azure DevOps data, so I spent about an hour writing a small program to replace his entire job. This triggered an arms race between myself and the coworker where they would slightly change what they added to the excel spreadsheet, and I would add that into the simple program (and then make it better). This went on for about 2 weeks until I was called into the manager’s office and was told that I was bullying the coworker by doing this, which I disagreed with.&lt;/p>
&lt;p>This lead me to thinking about, when does automating away a coworkers job cross the line into bullying. Automating a manual process in the workplace is not inherently bullying. However, if the automation is implemented without proper consideration for the impact on employees, it can potentially be perceived as bullying or create a hostile work environment. There are both positive and negative aspects (which include aspects that could be considered bullying), they are as follows:&lt;/p>
&lt;h2 id="positive-aspects-of-automating-a-manual-process-in-the-workplace">Positive Aspects of Automating a Manual Process in the Workplace:&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Increased Efficiency and Productivity:&lt;/strong> Automation can significantly enhance the speed and accuracy of tasks, leading to higher overall productivity.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cost Savings:&lt;/strong> By reducing the need for manual labor, companies can save on labor costs and potentially allocate resources to other areas of the business.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Quality Improvement:&lt;/strong> Automated processes often result in fewer errors and a higher level of consistency in output.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Time Allocation for Strategic Tasks:&lt;/strong> Employees can shift their focus from routine, manual tasks to more strategic, creative, or value-added activities that require human judgment and decision-making.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Competitive Advantage:&lt;/strong> Companies that leverage automation effectively can gain a competitive edge in their industry by being more agile and responsive to market demands.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="negative-aspects-of-automating-a-manual-process-in-the-workplace-potentially-involving-bullying">Negative Aspects of Automating a Manual Process in the Workplace (Potentially Involving Bullying):&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Job Displacement:&lt;/strong> Automation can lead to job loss for employees whose roles are automated, potentially causing financial stress and job insecurity. In some cases, if not handled with sensitivity and respect, the process of job displacement can be perceived as bullying.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Reskilling Challenges:&lt;/strong> Employees may face difficulties in transitioning to new roles or acquiring the necessary skills to work alongside automated systems. Inadequate support or a lack of resources for upskilling can contribute to feelings of isolation and frustration, resembling a form of workplace bullying.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Emotional Impact:&lt;/strong> Sudden or poorly managed automation can lead to feelings of exclusion, resentment, or fear among employees. If not addressed empathetically, these emotions can contribute to a hostile work environment.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Potential for Errors in Automation:&lt;/strong> Depending on the complexity of the process and the quality of the automation, there may be instances where errors occur, potentially causing disruptions. Blaming employees for errors caused by automation, rather than addressing the root cause, can be considered a form of bullying.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Over-Reliance on Technology:&lt;/strong> An excessive reliance on automated systems can leave a company vulnerable in situations where technology fails or requires maintenance. Pressuring employees to perform at an unsustainable pace due to overreliance on automation can be a form of bullying.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Cultural Resistance:&lt;/strong> Some employees may resist automation due to concerns about job security or a preference for traditional work methods. Disregarding their concerns and forcing the adoption of automation without addressing these fears can create a hostile work environment, resembling a form of bullying.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>It&amp;rsquo;s crucial for organizations to consider the potential negative impacts of automation and proactively address them with empathy and sensitivity to prevent any situation from resembling bullying in the workplace.&lt;/p>
&lt;p>&lt;strong>Update:&lt;/strong> Knowing what I know now (years later, and multiple companies later), I would have gone about this differently. Rather than just writing the application, I would have written a detailed case to my management. I would have detailed the amount of time taken to compile the spreadsheet by a staff member and the costs associated with that, and then detailed the amount of time and cost it would have taken to build the application and the ongoing maintenance costs. Companies talk money, and only money, not only would I not have been accused of bullying in this case, I would have been praised for saving a large source of inefficiency, and paid for writing the application.&lt;/p></description></item><item><title>Time Series Anomaly Detection with ML.NET in C#</title><link>https://www.darrenhorrocks.co.uk/anomaly-detection-ml-net/</link><pubDate>Tue, 19 Sep 2023 13:47:33 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/anomaly-detection-ml-net/</guid><description>&lt;p>ML.NET provides a very simple way of performing Anomaly Detection on random variables as long as they are independent and identically distributed (i.e. daily sales totals recorded at the end of each and every day). With it also being possible to detect anomalies without requiring past examples beyond your dataset.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>Before we get started, make sure you have the following prerequisites:&lt;/p>
&lt;ul>
&lt;li>Visual Studio with .NET Core or .NET Framework installed.&lt;/li>
&lt;li>Basic understanding of C# programming language.&lt;/li>
&lt;/ul>
&lt;h2 id="step-1-set-up-your-project">Step 1: Set Up Your Project&lt;/h2>
&lt;ul>
&lt;li>Open Visual Studio and create a new C# console application.&lt;/li>
&lt;/ul>
&lt;h2 id="step-2-install-mlnet-package">Step 2: Install ML.NET Package&lt;/h2>
&lt;ul>
&lt;li>Right-click on your project in Solution Explorer.&lt;/li>
&lt;li>Select &amp;ldquo;Manage NuGet Packages&amp;hellip;&amp;rdquo;&lt;/li>
&lt;li>Search for &amp;ldquo;Microsoft.ML&amp;rdquo; and install the latest version.&lt;/li>
&lt;/ul>
&lt;h2 id="step-3-import-necessary-libraries">Step 3: Import Necessary Libraries&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.ML;
&lt;span style="color:#66d9ef">using&lt;/span> Microsoft.ML.Data;
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-4-define-your-data-classes">Step 4: Define Your Data Classes&lt;/h2>
&lt;p>In this example, we&amp;rsquo;ll assume you have a class called &lt;code>TimeSeriesData&lt;/code> with two properties: &lt;code>TimeStamp&lt;/code> and &lt;code>Value&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">TimeSeriesData&lt;/span>
{
&lt;span style="color:#a6e22e"> [LoadColumn(0)]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> DateTime TimeStamp;
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [LoadColumn(1)]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">float&lt;/span> Value;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-5-load-and-prepare-your-data">Step 5: Load and Prepare Your Data&lt;/h2>
&lt;p>Load your time series data into a &lt;code>DataView&lt;/code> and split it into training and testing sets.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> context = &lt;span style="color:#66d9ef">new&lt;/span> MLContext();
&lt;span style="color:#66d9ef">var&lt;/span> data = context.Data.LoadFromTextFile&amp;lt;TimeSeriesData&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;path/to/your/data.csv&amp;#34;&lt;/span>, separatorChar: &lt;span style="color:#e6db74">&amp;#39;,&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> dataSplit = context.Data.TrainTestSplit(data);
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-6-define-your-model">Step 6: Define Your Model&lt;/h2>
&lt;p>Choose an anomaly detection algorithm. In this example, we&amp;rsquo;ll use the &lt;code>SrCnnEntireAnomalyDetector&lt;/code> algorithm.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> pipeline = context.Transforms.DetectAnomalyBySrCnn(&lt;span style="color:#e6db74">&amp;#34;Value&amp;#34;&lt;/span>, nameof(TimeSeriesPrediction.Prediction), &lt;span style="color:#ae81ff">10&lt;/span>)
.Append(context.Transforms.CopyColumns(&lt;span style="color:#e6db74">&amp;#34;Prediction&amp;#34;&lt;/span>, nameof(TimeSeriesPrediction.Prediction)));
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-7-train-your-model">Step 7: Train Your Model&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> model = pipeline.Fit(dataSplit.TrainSet);
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-8-make-predictions">Step 8: Make Predictions&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> predictions = model.Transform(dataSplit.TestSet);
&lt;span style="color:#66d9ef">var&lt;/span> anomalies = context.Data.FilterRowsByBoolColumn(predictions, &lt;span style="color:#e6db74">&amp;#34;Prediction&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-9-evaluate-the-model">Step 9: Evaluate the Model&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> metrics = context.AnomalyDetection.Evaluate(dataSplit.TestSet, &lt;span style="color:#e6db74">&amp;#34;Prediction&amp;#34;&lt;/span>);
Console.WriteLine(&lt;span style="color:#e6db74">$&amp;#34;AUC: {metrics.AreaUnderRocCurve}&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="step-10-interpret-the-results">Step 10: Interpret the Results&lt;/h2>
&lt;p>You can now use the &lt;code>anomalies&lt;/code> DataView to analyze and interpret the detected anomalies in your time series data.&lt;/p>
&lt;h2 id="step-11-deploy-and-use-your-model">Step 11: Deploy and Use Your Model&lt;/h2>
&lt;p>To use the model in your application, you can save it and load it as needed:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">context.Model.Save(model, dataSplit.TrainSet.Schema, &lt;span style="color:#e6db74">&amp;#34;model.zip&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> loadedModel = context.Model.Load(&lt;span style="color:#e6db74">&amp;#34;model.zip&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> modelSchema);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Congratulations! You&amp;rsquo;ve successfully implemented time series anomaly detection using ML.NET in C#. This tutorial provides a basic example, but you can further refine and expand your model based on your specific use case and data.&lt;/p>
&lt;p>Remember to replace the placeholders in the code (like &amp;ldquo;path/to/your/data.csv&amp;rdquo;) with your actual file paths and feel free to experiment with different algorithms, hyperparameters, and techniques to improve the accuracy of your anomaly detection model.&lt;/p></description></item><item><title>Company Culture: "No Blame" vs "Know Blame"</title><link>https://www.darrenhorrocks.co.uk/company-culture-no-blame-vs-know-blame/</link><pubDate>Tue, 19 Sep 2023 09:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/company-culture-no-blame-vs-know-blame/</guid><description>&lt;p>Companies often claim that the operate a &amp;ldquo;no blame culture&amp;rdquo;, and then joke tht its actually a &amp;ldquo;know blame culture&amp;rdquo;. A &amp;ldquo;no blame&amp;rdquo; culture and a &amp;ldquo;know blame&amp;rdquo; culture may sound similar, but they have significant differences in their underlying principles and how they are practiced within a software development organization. This is particularly notable in the software development industry due to its unique characteristics.&lt;/p>
&lt;h2 id="intent-and-focus">Intent and Focus&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>No Blame Culture:&lt;/strong> In a true &amp;ldquo;no blame&amp;rdquo; culture within a software development company, the focus is on identifying and fixing system-level issues that contribute to bugs, glitches, or project setbacks. The emphasis is on learning and refining processes, rather than assigning blame to individual developers.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Know Blame Culture:&lt;/strong> In a &amp;ldquo;know blame&amp;rdquo; culture, the main goal is to pinpoint specific developers responsible for errors or delays. The focus is on holding individuals accountable, often with the intention of reprimanding or punishing them.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>The prevalence of a &amp;ldquo;know blame&amp;rdquo; culture in software companies can be attributed to several industry-specific factors. In software development, output is often easily quantifiable, which makes it more straightforward to assign individual responsibility for bugs, defects, or project delays. The complex and interdependent nature of code can also lead to a tendency to focus on individual blame when issues arise.&lt;/p>
&lt;h2 id="response-to-failure">Response to Failure&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>No Blame Culture:&lt;/strong> When a coding error or project setback occurs, the emphasis is on understanding the underlying causes and finding solutions. The focus is on preventing similar incidents in the future.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Know Blame Culture:&lt;/strong> In a &amp;ldquo;know blame&amp;rdquo; culture, the immediate response to a mistake may be to identify the responsible developer and take disciplinary action.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="psychological-safety">Psychological Safety&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>No Blame Culture:&lt;/strong> A &amp;ldquo;no blame&amp;rdquo; culture fosters an environment of psychological safety, where developers feel comfortable admitting mistakes, asking for help, or reporting problems without fear of reprisal.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Know Blame Culture:&lt;/strong> A &amp;ldquo;know blame&amp;rdquo; culture can create a tense atmosphere where developers may be hesitant to admit their mistakes or report issues due to the fear of punishment.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>The competitive nature of the software industry can further exacerbate this, creating a results-driven culture where the focus is on delivering high-quality products quickly, potentially overshadowing the importance of a blame-free environment.&lt;/p>
&lt;h2 id="leadership-behavior">Leadership Behavior&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>No Blame Culture:&lt;/strong> Leaders in a &amp;ldquo;no blame&amp;rdquo; culture encourage open communication, focus on collaborative problem-solving, and support developers in learning from mistakes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Know Blame Culture:&lt;/strong> Leaders in a &amp;ldquo;know blame&amp;rdquo; culture may engage in finger-pointing, publicly reprimand developers, or create an atmosphere of fear and blame.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="learning-and-improvement">Learning and Improvement&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>No Blame Culture:&lt;/strong> Continuous improvement and learning are fundamental values. The company invests in training, feedback mechanisms, and process enhancements to prevent future errors.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Know Blame Culture:&lt;/strong> While individuals may be held accountable for their mistakes, there may be little emphasis on systemic improvements or learning from the experiences.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Changing ingrained cultural norms, especially in older or more established software companies, can be a slow process. However, many progressive companies within the industry are actively working to shift towards a more constructive &amp;ldquo;no blame&amp;rdquo; culture that prioritizes learning, collaboration, and systemic improvement over assigning individual blame.&lt;/p>
&lt;h2 id="feedback-mechanisms">Feedback Mechanisms&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>No Blame Culture:&lt;/strong> Feedback is constructive, focused on improving code quality, processes, and project outcomes. It aims to help developers and the organization grow and learn from experiences.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Know Blame Culture:&lt;/strong> Feedback may be punitive, aimed at reprimanding or punishing developers for their mistakes.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>In assessing the actual culture of a software development company, it&amp;rsquo;s crucial to look beyond stated values and observe how issues are handled in practice, how leaders respond, and whether there is a genuine commitment to continuous improvement in the development process. Remember, while the prevalence of a &amp;ldquo;know blame&amp;rdquo; culture in some software companies is influenced by industry-specific factors, many forward-thinking organizations are actively working to shift towards a more constructive and growth-oriented approach.&lt;/p></description></item><item><title>How a Zero Trust Policy Hampers Software Developer Productivity</title><link>https://www.darrenhorrocks.co.uk/how-zero-trust-hampers-software-developers/</link><pubDate>Mon, 18 Sep 2023 08:19:18 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/how-zero-trust-hampers-software-developers/</guid><description>&lt;p>In recent years, the Zero Trust security model has gained prominence as a robust approach to safeguarding digital assets. However, when applied within a software development company, this policy can inadvertently hinder the productivity of software developers. This article delves into the specific challenges that arise when implementing a Zero Trust policy in this context, shedding light on why it may not be the most conducive approach.&lt;/p>
&lt;h2 id="restricting-development-environment-flexibility">Restricting Development Environment Flexibility&lt;/h2>
&lt;p>Software development thrives on flexibility. Developers often need the freedom to swiftly set up new environments, integrate various tools, and experiment with different configurations. A Zero Trust policy, characterized by stringent access controls and micro-segmentation, can impede this agile development process. It may lead to delays and frustrations for developers who require timely access to resources.&lt;/p>
&lt;h2 id="curbing-collaboration-and-knowledge-sharing">Curbing Collaboration and Knowledge Sharing&lt;/h2>
&lt;p>Collaboration is the lifeblood of software development. Developers frequently engage in code sharing, joint debugging sessions, and collaborative code reviews. The implementation of a Zero Trust policy introduces barriers to this essential teamwork. For example, the rigorous access controls may hinder the seamless sharing of resources, leading to a more disjointed and less productive development process.&lt;/p>
&lt;h2 id="navigating-authentication-overload">Navigating Authentication Overload&lt;/h2>
&lt;p>Modern software development often involves integrating with a multitude of third-party services, APIs, and libraries. A Zero Trust policy can complicate this process by necessitating extensive authentication and authorization procedures for every external service. This added layer of security may impede the timely integration of crucial components, potentially delaying project milestones.&lt;/p>
&lt;h2 id="balancing-security-and-developer-efficiency">Balancing Security and Developer Efficiency&lt;/h2>
&lt;p>While security is paramount, there&amp;rsquo;s a delicate balance to be struck between security measures and productivity. A Zero Trust policy, if not thoughtfully implemented, can tilt the scales too far towards security, potentially sacrificing the speed and efficiency of the development process.&lt;/p>
&lt;h2 id="managing-complexity-in-security-configuration">Managing Complexity in Security Configuration&lt;/h2>
&lt;p>Implementing a Zero Trust policy demands a substantial investment in time and resources. It involves configuring and overseeing an array of security tools, policies, and protocols. In a software development company, where the primary focus should be on creating innovative solutions, this added complexity can divert resources away from the core mission.&lt;/p>
&lt;h2 id="developer-morale-and-user-experience">Developer Morale and User Experience&lt;/h2>
&lt;p>Excessive security measures can erode the user experience for developers. Constantly navigating authentication barriers can lead to frustration, which in turn can impact job satisfaction. Over time, this could even contribute to higher turnover rates within the development team.&lt;/p>
&lt;h2 id="potential-over-reliance-on-technology">Potential Over-Reliance on Technology&lt;/h2>
&lt;p>A Zero Trust policy often relies heavily on advanced technology solutions, including identity and access management tools, network monitoring systems, and more. Relying too heavily on technology can create a false sense of security. It&amp;rsquo;s crucial to remember that no system is infallible, and human factors should not be overlooked.&lt;/p>
&lt;h2 id="balancing-security-with-developer-productivity">Balancing Security with Developer Productivity&lt;/h2>
&lt;p>While a Zero Trust policy has its merits, applying it within a software development company requires careful consideration of the specific needs and workflows of developers. Striking a balance between security and productivity is essential for fostering an environment where developers can innovate efficiently and effectively, without unnecessary constraints.&lt;/p></description></item><item><title>The Pitfalls of Overmanaging Software Developers</title><link>https://www.darrenhorrocks.co.uk/the-pitfalls-of-overmanaging-software-developers/</link><pubDate>Fri, 15 Sep 2023 11:03:54 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/the-pitfalls-of-overmanaging-software-developers/</guid><description>&lt;p>In the fast-paced world of software development, achieving high levels of productivity is paramount for successful project completion. However, counterintuitively, overmanaging software developers can often lead to reduced efficiency and hinder the overall progress of a project. This article explores the reasons behind this phenomenon and suggests alternative approaches to foster a more productive work environment.&lt;/p>
&lt;h2 id="micromanagement-stifles-creativity">Micromanagement stifles creativity&lt;/h2>
&lt;p>One of the primary drawbacks of overmanagement is the stifling of creativity. Software development is a field that demands a degree of creative problem-solving. When developers are constantly and overly micromanaged, they are less likely to think outside the box and create innovative solutions. This leads to a decrease in the quality and originality of the code produced.&lt;/p>
&lt;h2 id="loss-of-autonomy-and-ownership">Loss of autonomy and ownership&lt;/h2>
&lt;p>Excessive oversight can strip developers of their sense of ownership over their work. Autonomy is a powerful motivator in the software development industry. When developers have the freedom and discretion to make decisions about how to approach a task or solve a problem, they feel more invested in the outcome. Overmanagement can erode this sense of ownership, leading to a decrease in commitment and motivation.&lt;/p>
&lt;h2 id="reduction-in-problem-solving-skills">Reduction in problem-solving skills&lt;/h2>
&lt;p>Problem-solving is a critical skill for software developers. When managers solve every problem for their team, developers are deprived of the opportunity to learn and grow. It&amp;rsquo;s essential to allow developers to grapple with challenges on their own, as this fosters independence and helps them develop crucial problem-solving skills.&lt;/p>
&lt;h2 id="demotivation-and-burnout">Demotivation and burnout&lt;/h2>
&lt;p>Overmanagement can lead to demotivation and, ultimately, burnout among developers. Constant scrutiny and a lack of trust can create a stressful work environment. When developers feel that their every move is being watched, it can lead to anxiety and a decreased sense of job satisfaction. This, in turn, can result in higher turnover rates and a loss of valuable talent.&lt;/p>
&lt;h2 id="reduced-adaptability-and-responsiveness">Reduced adaptability and responsiveness&lt;/h2>
&lt;p>In today&amp;rsquo;s dynamic tech landscape, adaptability is key. Overmanaging can impede a team&amp;rsquo;s ability to respond quickly to changing requirements or emerging technologies. When developers are not empowered to make decisions and adjustments on their own, the development process becomes slow and inflexible.&lt;/p>
&lt;h2 id="inefficiencies-in-communication">Inefficiencies in communication&lt;/h2>
&lt;p>Excessive management can lead to bottlenecks in communication. Instead of direct communication between team members, information has to flow through multiple layers of management. This can result in delays and misinterpretations, further impeding progress.&lt;/p>
&lt;h2 id="finding-the-right-balance">Finding the Right Balance&lt;/h2>
&lt;p>Instead of micromanaging, it&amp;rsquo;s important to establish a culture of trust, autonomy, and open communication. Here are some strategies to strike the right balance:&lt;/p>
&lt;ul>
&lt;li>Set clear expectations: Provide a clear roadmap and define project objectives. This gives developers a sense of direction without stifling creativity.&lt;/li>
&lt;li>Encourage autonomy: Allow developers to take ownership of their work. Provide guidance and support but let them make decisions about how to approach tasks.&lt;/li>
&lt;li>Foster a culture of learning: Encourage developers to seek out solutions independently. Provide opportunities for professional growth and skill development.&lt;/li>
&lt;li>Maintain open communication channels: Foster an environment where developers feel comfortable sharing their ideas, concerns, and challenges.&lt;/li>
&lt;li>Trust your team: Trust is the cornerstone of a productive team. When developers feel trusted, they are more likely to take initiative and demonstrate a high level of accountability.&lt;/li>
&lt;/ul>
&lt;p>In conclusion, while managers need to provide guidance and support, overmanaging software developers can lead to reduced productivity, creativity, and morale. Striking a balance between oversight and autonomy is crucial for fostering a productive and innovative development team.&lt;/p></description></item><item><title>Linus Tech Tips, Gamers Nexus, Ethics and Responsibility</title><link>https://www.darrenhorrocks.co.uk/linus-tech-tips-gamers-nexus-ethics-and-responsibility/</link><pubDate>Tue, 15 Aug 2023 14:04:59 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/linus-tech-tips-gamers-nexus-ethics-and-responsibility/</guid><description>&lt;p>Yesterday, Gamers Nexus posted a video titled &lt;a href="https://www.youtube.com/watch?v=FGW3TPytTjc">&amp;ldquo;The Problem with Linus Tech Tips: Accuracy, Ethics, &amp;amp; Responsibility&amp;rdquo;&lt;/a>, which addressed several concerns that GN have with Linus Tech Tips, the LMG Labs, and Linus Himself.&lt;/p>
&lt;p>This then sparked a long thread on the &lt;a href="https://linustechtips.com/topic/1526180-gamers-nexus-alleges-lmg-has-insufficient-ethics-and-integrity">LTT forums&lt;/a> where &lt;a href="https://linustechtips.com/topic/1526180-gamers-nexus-alleges-lmg-has-insufficient-ethics-and-integrity/page/16/#comment-16078641">Linus himself responded&lt;/a> to the video and several of the comments made by the forum users around the video.&lt;/p>
&lt;p>With this post, I would mostly like to address several things Linus stated in his response to the video.&lt;/p>
&lt;blockquote>
&lt;p>There won&amp;rsquo;t be a big WAN Show segment about this or anything. Most of what I have to say, I&amp;rsquo;ve already said, and I&amp;rsquo;ve done so privately.&lt;/p>
&lt;/blockquote>
&lt;p>This is a public issue, the general public have now seen what Steve and GN have to say, and attempting to keep the responses to Steve private, suggest that there are things that Linus does not want to be in the public, suggesting intentional wrong doing, rather than simply making mistakes.&lt;/p>
&lt;blockquote>
&lt;p>To Steve, I expressed my disappointment that he didn&amp;rsquo;t go through proper journalistic practices in creating this piece. He has my email and number (along with numerous other members of our team) and could have asked me for context that may have proven to be valuable (like the fact that we didn&amp;rsquo;t &amp;lsquo;sell&amp;rsquo; the monoblock, but rather auctioned it for charity due to a miscommunication.&lt;/p>
&lt;/blockquote>
&lt;p>Firstly, to continue what i said on the previous quote, why are there things Linus wants to keep private here on an issue that is already in the public eye? Secondly, contacting Linus personally, and resolving it quietly. is the exact opposite of &amp;ldquo;proper journalistic practices&amp;rdquo;. Journalists can ask the subject of a story for comment, but it is not at all required, especially if the request for comment will affect the story or affect the journalist writing the story. On the point of &amp;ldquo;selling&amp;rdquo; the monoblock, legally, they accepted cash for that waterblock, and then donated the cash straight to charity, that is a sale in Canada, the USA, the UK and almost anywhere else in the world.&lt;/p>
&lt;blockquote>
&lt;p>To my team (and my CEO&amp;rsquo;s team, but realistically I was at the helm for all of these errors, so I need to own it), I stressed the importance of diligence in our work because there are so many eyes on us. We are going through some growing pains - we&amp;rsquo;ve been very public about them in the interest of transparency - and it&amp;rsquo;s clear we have some work to do on internal processes and communication. We have already been doing a lot of work internally to clean up our processes, but these things take time. Rome wasn&amp;rsquo;t built in a day, but that&amp;rsquo;s no excuse for sloppiness&lt;/p>
&lt;/blockquote>
&lt;p>Linus appears here to be suggesting that the LMG Lab is a &amp;ldquo;work in progress&amp;rdquo;, yet they are presenting the numbers without any disclaimer stating that they could be incorrect, they are stating the numbers as absolute fact. The Lab cannot both be a &amp;ldquo;work in progress&amp;rdquo; and absolute fact at the same time, if Linus and the new CEO want to &amp;ldquo;take ownership&amp;rdquo; of this, any data coming from the labs in previous videos and all future videos (until these issues have been ironed out) should come with a visible, and audible disclaimer stating that the labs is a work in progress.&lt;/p>
&lt;p>Linus has also stated on the WAN Show that he is unwilling to spend &amp;ldquo;100, 200, 500 dollars&amp;rdquo; on fixing these mistakes, before they are published, on videos that make them 100s of 1000s of dollars. Why would such a trivial amount of money not be worth spending on factual accuracy? What money has been skimped on elsewhere on factual inaccuracies that have not been picked up on? Linus&amp;rsquo;s stance on this opens up more questions than it answers.&lt;/p>
&lt;blockquote>
&lt;p>Now, for my community, all I can say is the same things I always say. We know that we&amp;rsquo;re not perfect. We wear our imperfection on our sleeves in the interest of ensuring that we stay accountable to you. But it&amp;rsquo;s sad and unfortunate when this transparency gets warped into a bad thing. The Labs team is hard at work hard creating processes and tools to generate data that will benefit all consumers - a work in progress that is very much not done and that we&amp;rsquo;ve communicated needs to be treated as such.&lt;/p>
&lt;/blockquote>
&lt;p>There is no transparency, it took somebody else to point out all of LMGs mistakes to prompt a post from Linus. The &amp;ldquo;corrections&amp;rdquo; they have made, are made in the laziest and cheapest and least visible way possible. Are LMG looking to strive for accuracy, or a better bottom line?&lt;/p>
&lt;blockquote>
&lt;p>With all of that said, I still disagree that the Billet Labs video (not the situation with the return, which I&amp;rsquo;ve already addressed above) is an &amp;lsquo;accuracy&amp;rsquo; issue. It&amp;rsquo;s more like I just read the room wrong. We COULD have re-tested it with perfect accuracy, but to do so PROPERLY - accounting for which cases it could be installed in (none) and which radiators it would be plumbed with (again&amp;hellip; mystery) would have been impossible&amp;hellip; and also didn&amp;rsquo;t affect the conclusion of the video&amp;hellip; OR SO I THOUGHT&amp;hellip;&lt;/p>
&lt;/blockquote>
&lt;p>If LMG/Linus now know that the video is entirely factually inaccurate, why does the video still remain public? Why has there not been any insert into the video to let consumers know that the entire content of the video is incorrect? Why has LMG not added inserts into the video to show what they did wrong? Again, this is down to the bottom line of the company being a bigger priority than factual accuracy.&lt;/p>
&lt;blockquote>
&lt;p>Adam and I were talking about this today. He advocated for re-testing it regardless of how non-viable it was as a product at the time and I think he expressed really well today why it mattered. It was like making a video about a supercar. It doesn&amp;rsquo;t mater if no one watching will buy it. They just wanna see it rip. I missed that, but it wasn&amp;rsquo;t because I didn&amp;rsquo;t care about the consumer.. it was because I was so focused on how this product impacted a potential buyer. Either way, clearly my bad, but my intention was never to harm Billet Labs&lt;/p>
&lt;/blockquote>
&lt;p>Here Linus states that other members of the team, specifically the writer and co-presenter of the video, were not pleased with the video and its findings and wated to retest, and Linus vetoed that and let the video go out as it was, and still allows it to remain live to this day (at the time of writing this post). If the intention is not to harm billet labs, then why offer such harsh opinions on known bad data, and why leave the known bad video, up and live?&lt;/p>
&lt;blockquote>
&lt;p>Either way, I&amp;rsquo;m sorry I got the community&amp;rsquo;s priorities mixed-up on this one, and that we didn&amp;rsquo;t show the Billet in the best light. Our intention wasn&amp;rsquo;t to hurt anyone. We wanted no one to buy it (because it&amp;rsquo;s an egregious waste of money no matter what temps it runs at) and we wanted Billet to make something marketable (so they can, y&amp;rsquo;know, eat).&lt;/p>
&lt;/blockquote>
&lt;p>Linus is only sorry now that he has been called out, hes stated several times on the WAN show that he doesnt want to pay trivial amounts of money to fix factual inaccuracies in videos, hes left several known factually inaccurate videos live. Linus and LMG need to take real action on this rather than making a placebo statement to try and quell the rage from the community.&lt;/p>
&lt;hr>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>In conclusion, I dont dislike Linus, I dont dislike LMG/LTT, and I dont dislike what LMG are trying to do with the Labs, but I do think they need to drastically clean up their act in order to stop themselves from becoming an industry joke and having the labs be dead before they even get into the full swing. They are the largest tech youtube channel where a lot of people get their information from, they influence a lot of people and what those people buy, and can influence whether or not a small company lives or dies. With that power, they need to be incredibly careful, or they will continue to be called out and will suffer unfixable reputational damage.&lt;/p>
&lt;p>Hopefully LMG fix their issues internally, and a proper response is made, and the previously known factually inaccurate videos are fixed, and future videos are fixed correctly with visible and audible corrections.&lt;/p></description></item><item><title>5 Reasons C# Is Better Than Java</title><link>https://www.darrenhorrocks.co.uk/5-reasons-c-sharp-is-better-than-java/</link><pubDate>Tue, 15 Aug 2023 13:59:17 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/5-reasons-c-sharp-is-better-than-java/</guid><description>&lt;p>When it comes to choosing the right programming language, developers often find themselves faced with a myriad of options. Among these, C# and Java are two prominent contenders, each with its own strengths and weaknesses. In this article, we&amp;rsquo;ll delve into five compelling reasons why C# outshines Java, making it an exceptional choice for modern software development.&lt;/p>
&lt;p>Here are 5 reasons why choosing C# is a better idea than choosing Java.&lt;/p>
&lt;h2 id="elegant-syntax-and-modern-features">Elegant Syntax and Modern Features:&lt;/h2>
&lt;p>C# boasts an elegant and concise syntax that enhances code readability and reduces the potential for errors. Its modern features, such as properties, events, and LINQ (Language Integrated Query), contribute to more efficient and expressive code. On the other hand, Java&amp;rsquo;s syntax can be more verbose and less intuitive, often requiring extra lines of code to accomplish similar tasks.&lt;/p>
&lt;h2 id="seamless-integration-with-microsoft-ecosystem">Seamless Integration with Microsoft Ecosystem:&lt;/h2>
&lt;p>C# is a language closely associated with the Microsoft ecosystem, offering seamless integration with various Microsoft technologies, such as .NET, Azure, and Visual Studio. Developers working on Windows applications, web services, or cloud solutions can leverage these integrations to streamline their development process. While Java is cross-platform, C#&amp;rsquo;s integration with Microsoft tools and services provides a more cohesive environment for Windows-centric projects.&lt;/p>
&lt;h2 id="powerful-development-environment">Powerful Development Environment:&lt;/h2>
&lt;p>Visual Studio, the primary integrated development environment (IDE) for C#, is renowned for its robust set of tools, debugging capabilities, and performance profiling features. It provides developers with an efficient workflow and a rich ecosystem of extensions. While Java has IDEs like IntelliJ IDEA and Eclipse, Visual Studio&amp;rsquo;s comprehensive toolset gives C# an edge in terms of productivity and code quality.&lt;/p>
&lt;h2 id="language-innovations-and-regular-updates">Language Innovations and Regular Updates:&lt;/h2>
&lt;p>C# has been consistently evolving, introducing new language features and enhancements to keep up with modern programming paradigms. With the introduction of C# 9 and beyond, features like record types, pattern matching, and improved nullable reference types have made C# even more powerful and expressive. Java, while also evolving, might have a more gradual adoption rate for new language features due to its larger and more diverse community.&lt;/p>
&lt;h2 id="unified-development-stack">Unified Development Stack:&lt;/h2>
&lt;p>C# offers a unified development stack through the .NET framework, allowing developers to build a wide range of applications, including desktop, web, mobile, and cloud-based solutions. This unified approach simplifies the development process by providing a consistent set of libraries, tools, and APIs. In contrast, Java developers might need to work with different frameworks and libraries for various application types, potentially leading to a steeper learning curve and increased complexity.&lt;/p>
&lt;h2 id="in-summary">In Summary&amp;hellip;&lt;/h2>
&lt;p>While both C# and Java have their merits, C# stands out as a language that combines elegant syntax, powerful tooling, seamless integration with the Microsoft ecosystem, and regular updates to enhance its capabilities. These factors make C# a compelling choice for developers looking to build robust and efficient software solutions in a modern development landscape. As technology continues to evolve, C# remains at the forefront, offering a vibrant and dynamic environment for innovation and creation.&lt;/p></description></item><item><title>Yes You Can Block Elon Musk on Twitter</title><link>https://www.darrenhorrocks.co.uk/yes-you-can-block-elon-musk-on-twitter/</link><pubDate>Fri, 28 Jul 2023 21:21:46 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/yes-you-can-block-elon-musk-on-twitter/</guid><description>&lt;p>After being told by somebody on discord that it &amp;ldquo;is impossible to block elon musk on twitter&amp;rdquo;, when in fact, it is quite simple. I decided to make this post and this video to show just how simple it is.&lt;/p>
&lt;p>It is actually quite simple when you know how:&lt;/p>
&lt;ol>
&lt;li>Search for &amp;ldquo;elon musk&amp;rdquo;&lt;/li>
&lt;li>Click &amp;ldquo;&amp;hellip;&amp;rdquo; button&lt;/li>
&lt;li>Click &amp;ldquo;block&amp;rdquo;&lt;/li>
&lt;li>DONE!&lt;/li>
&lt;/ol>
&lt;p>Or watch the youtube video below:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/OhMVyJgHhNQ" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div></description></item><item><title>How to Become a Better Software Developer With These GitHub Repos</title><link>https://www.darrenhorrocks.co.uk/become-a-better-developer-with-these-github-repos/</link><pubDate>Wed, 26 Jul 2023 10:23:44 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/become-a-better-developer-with-these-github-repos/</guid><description>&lt;p>GitHub hosts millions of different repositories, most are projects that developers are working on, but a fraction of them are valuable resources for learning. Over time, I have collected a list of repos that contain these valuable resources.&lt;/p>
&lt;h2 id="every-programmer-should-knowhttpsgithubcommtdvioevery-programmer-should-know">&lt;a href="https://github.com/mtdvio/every-programmer-should-know">Every Programmer Should Know&lt;/a>&lt;/h2>
&lt;p>No matter how much you know, you never know enough.&lt;/p>
&lt;p>&amp;ldquo;Every Programmer Should Know&amp;rdquo; is a collection of mostly technical things that all software developers should know. With this repository, you might learn a few new tricks, and this alone justifies spending some time on this collection.&lt;/p>
&lt;h2 id="build-your-own-xhttpsgithubcomcodecrafters-iobuild-your-own-x">&lt;a href="https://github.com/codecrafters-io/build-your-own-x">Build your own X&lt;/a>&lt;/h2>
&lt;p>Have you ever tried to create your own operating system? Or your own BitTorrent client?&lt;/p>
&lt;p>Build your own X tries to close the knowledge gap in implementing some basic but also some pretty advanced topics. This whole repository shows you that anybody can create anything if they are willing.&lt;/p>
&lt;h2 id="awesome-guidelineshttpsgithubcomkristoriesawesome-guidelines">&lt;a href="https://github.com/Kristories/awesome-guidelines">Awesome Guidelines&lt;/a>&lt;/h2>
&lt;p>Awesome Guidelines is a little different. Instead of distilling knowledge into a collection or a book itself, it gives you some of the most basic foundations a programmer regularly needs: Coding and formatting guidelines.&lt;/p>
&lt;p>Code is read way more often than it is actually written. This alone justifies formatting your code in some way or the other – at least to the extent that you and everyone you work with can agree on how your code should look.&lt;/p>
&lt;h2 id="professional-programminghttpsgithubcomcharlaxprofessional-programming">&lt;a href="https://github.com/charlax/professional-programming">Professional Programming&lt;/a>&lt;/h2>
&lt;p>Professional Programming is a classic resource collection with many tips on outstanding books, many more worthwhile articles to consume, and a link for nearly everything you can probably imagine.&lt;/p>
&lt;h2 id="project-based-learninghttpsgithubcompractical-tutorialsproject-based-learning">&lt;a href="https://github.com/practical-tutorials/project-based-learning">Project Based Learning&lt;/a>&lt;/h2>
&lt;p>Practice is the best way to learn software development. Repeatedly solving specific problems fortifies your knowledge and gives your brain the reason to memorize all those concepts you apply during your work.&lt;/p>
&lt;p>Project Based Learning is a list of programming tutorials that shows aspiring software developers how to build an application from scratch. It contains projects in many different languages.&lt;/p>
&lt;h2 id="the-system-design-primerhttpsgithubcomdonnemartinsystem-design-primer">&lt;a href="https://github.com/donnemartin/system-design-primer">The System Design Primer&lt;/a>&lt;/h2>
&lt;p>System design is one of those fields where practice especially makes you better. Theory often lacks behind real-world use cases and issues you encounter in the wild.&lt;/p>
&lt;p>This repository, however, is an incredible example of how to teach the very practical domain of system design with Python. If you manage to finish going through this repository, you’ll be a pretty dangerous software engineer who knows their way around system design way better than many of their peers.&lt;/p>
&lt;h2 id="coding-interview-universityhttpsgithubcomdonnemartinsystem-design-primer">&lt;a href="https://github.com/donnemartin/system-design-primer">Coding Interview University&lt;/a>&lt;/h2>
&lt;p>Becoming a software developer at a large company can be a challenging task.&lt;/p>
&lt;p>Tech interviews can be daunting and are often nothing like the actual job. Some even go so far as to say that you need a whole different set of skills to pass a tech interview than you need to do the job.&lt;/p>
&lt;p>Coding Interview University is a repository that aims at precisely guiding you through the process of becoming competent in tech interview situations, all while additionally teaching you many crucial computer science fundamentals.&lt;/p>
&lt;h2 id="30-seconds-of-codehttpsgithubcom30-seconds30-seconds-of-code">&lt;a href="https://github.com/30-seconds/30-seconds-of-code">30 seconds of code&lt;/a>&lt;/h2>
&lt;p>30 seconds of code contains JavaScript code snippets.&lt;/p>
&lt;p>This repository is the perfect bookmark if you ever get stuck. Just jump into it and look at whether there is already a solution for your problem.&lt;/p></description></item><item><title>New and Interesting Features in C# 12</title><link>https://www.darrenhorrocks.co.uk/whats-interesting-in-csharp-12/</link><pubDate>Thu, 13 Jul 2023 00:08:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/whats-interesting-in-csharp-12/</guid><description>&lt;p>C# 12 introduces several features aimed at laying the foundation for future performance enhancements. These features include easy access to inline arrays, an experimental feature called interceptors, and enhancements to the &lt;code>nameof&lt;/code> keyword.&lt;/p>
&lt;p>Inline arrays allow libraries to use them more conveniently without additional effort. They provide efficient and type-safe access to a contiguous sequence of primitives. The compiler generates different IL (Intermediate Language) code to access inline arrays, resulting in performance gains without requiring changes to your code.&lt;/p>
&lt;p>Interceptors are an experimental feature intended for advanced scenarios, particularly for better ahead-of-time compilation (AOT). They allow specific method calls to be rerouted to different code. Interceptors are primarily used in conjunction with source generators. By enabling interceptors in your project file, you can benefit from code patterns such as optimized static code generation, direct call bypassing allocation and indirection, vectorization, dependency graph resolution, query translation, and compile-time serialization.&lt;/p>
&lt;p>It&amp;rsquo;s important to note that interceptors are experimental and should not be used in production. They may undergo changes or be removed in future versions of C# and .NET.&lt;/p>
&lt;p>To access C# 12 features, you need to install the latest Visual Studio preview or the latest .NET SDK. Additionally, you&amp;rsquo;ll need to set the language version of your project to &amp;ldquo;preview&amp;rdquo; in the project file. Interceptors require an additional flag in the project file to enable them.&lt;/p>
&lt;p>Read more over on the &lt;a href="https://devblogs.microsoft.com/dotnet/new-csharp-12-preview-features/">microsoft dev blogs&lt;/a>.&lt;/p></description></item><item><title>Creating Windows and Linux background services in dotnet 6.0/7.0</title><link>https://www.darrenhorrocks.co.uk/dotnet-7-background-worker-services/</link><pubDate>Sun, 02 Jul 2023 22:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/dotnet-7-background-worker-services/</guid><description>&lt;p>There are times when you need an application to just sit there in the background and deal with the same repetitive task over and over again.&lt;/p>
&lt;p>In dotnet core 3.0, a new type of template was introduced, &amp;ldquo;Worker Service&amp;rdquo;, which is a very easy way of creating Windows Services and Linux Daemons, and it is just as easy in dotnet 6 and 7 (if not, even easier).&lt;/p>
&lt;h2 id="the-basics">The Basics&lt;/h2>
&lt;p>The &amp;ldquo;worker&amp;rdquo; template can be accessed in one of two ways:&lt;/p>
&lt;ol>
&lt;li>via the command line&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmd" data-lang="cmd">&lt;span style="color:#66d9ef">mkdir&lt;/span> myNewWorker
&lt;span style="color:#66d9ef">cd&lt;/span> myNewWorker
dotnet new worker
&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>via Visual Studio&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="/images/dotnet-new-worker-figure-1.png" alt="Visual Studio new background worker project example" title="Visual Studio new background worker project example">&lt;/p>
&lt;p>Either way, you end up with the same basic template to start with, which is your Startup class, and your Worker class.&lt;/p>
&lt;h2 id="the-startup-class">The Startup Class&lt;/h2>
&lt;p>The startup class is your entry point to the executable
In the initial template, we are&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> IHostBuilder CreateHostBuilder(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">return&lt;/span> Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =&amp;gt;
{
services.AddHostedService&amp;lt;Worker&amp;gt;();
});
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first thing we need to do is add the generic host library nuget package &lt;code>Microsoft.Extensions.Hosting&lt;/code> and switch the SDK to the worker SDK.&lt;/p>
&lt;p>You can do this by opening up your csproj file and making the following changes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk.Worker&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>net7.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PackageReference&lt;/span> &lt;span style="color:#a6e22e">Include=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.Extensions.Hosting&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, we need to add the following lines to our &lt;code>CreateHostBuilder&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> IHostBuilder CreateHostBuilder(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">return&lt;/span> Host.CreateDefaultBuilder(args)
.UseSystemd()
.UseWindowsService()
.ConfigureServices((hostContext, services) =&amp;gt;
{
services.AddHostedService&amp;lt;Worker&amp;gt;();
});
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-worker-class">The Worker class&lt;/h2>
&lt;p>Lastly, we can create our background service worker class, which is executed asynchronously, has logging, and has configuration built in too.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp"> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Worker&lt;/span> : BackgroundService
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> ILogger&amp;lt;Worker&amp;gt; &lt;span style="color:#ae81ff">_l&lt;/span>ogger;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> IConfiguration configuration;
&lt;span style="color:#66d9ef">public&lt;/span> Worker(ILogger&amp;lt;Worker&amp;gt; logger, IConfiguration configuration)
{
&lt;span style="color:#ae81ff">_l&lt;/span>ogger = logger;
&lt;span style="color:#66d9ef">this&lt;/span>.configuration = configuration;
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task ExecuteAsync(CancellationToken stoppingToken)
{
&lt;span style="color:#66d9ef">while&lt;/span> (!stoppingToken.IsCancellationRequested)
{
&lt;span style="color:#66d9ef">await&lt;/span> Task.Delay(&lt;span style="color:#ae81ff">1000&lt;/span>, stoppingToken);
}
}
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>C# Basics: Generate Random Numbers</title><link>https://www.darrenhorrocks.co.uk/csharp-basics-generate-random-numbers/</link><pubDate>Sat, 01 Jul 2023 11:27:32 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-basics-generate-random-numbers/</guid><description>&lt;p>Random numbers play a crucial role in various applications, from gaming to cryptography. In C#, generating random numbers is made easy with the help of the built-in Random class. In this article, we will explore different methods of generating random numbers in C# and discuss their applications.&lt;/p>
&lt;h2 id="method-1-using-the-random-class">Method 1: Using the Random Class&lt;/h2>
&lt;p>The most common way to generate random numbers in C# is by utilizing the Random class from the System namespace. The Random class provides a convenient interface to generate random numbers based on a seed value. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">Random random = &lt;span style="color:#66d9ef">new&lt;/span> Random(); &lt;span style="color:#75715e">// Create an instance of the Random class
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#66d9ef">int&lt;/span> randomNumber = random.Next(); &lt;span style="color:#75715e">// Generate a random integer
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">double&lt;/span> randomDouble = random.NextDouble(); &lt;span style="color:#75715e">// Generate a random double between 0.0 and 1.0
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The Next() method generates a non-negative random integer, while NextDouble() generates a random double value between 0.0 and 1.0. By default, the Random class uses the current time as the seed value. However, you can also specify a seed value explicitly to generate the same sequence of random numbers.&lt;/p>
&lt;h2 id="method-2-generating-random-numbers-within-a-range">Method 2: Generating Random Numbers within a Range&lt;/h2>
&lt;p>In many scenarios, we need random numbers within a specific range. The Random class provides an overloaded Next() method to accomplish this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">int&lt;/span> min = &lt;span style="color:#ae81ff">1&lt;/span>;
&lt;span style="color:#66d9ef">int&lt;/span> max = &lt;span style="color:#ae81ff">100&lt;/span>;
&lt;span style="color:#66d9ef">int&lt;/span> randomInRange = random.Next(min, max); &lt;span style="color:#75715e">// Generate a random integer within the specified range
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The Next(min, max) method generates a random integer greater than or equal to min and less than max. By adjusting the values of min and max, you can generate random numbers within any desired range&lt;/p>
&lt;h2 id="method-3-generating-random-numbers-using-cryptoserviceprovider">Method 3: Generating Random Numbers Using CryptoServiceProvider&lt;/h2>
&lt;p>If you require more secure random numbers for cryptographic purposes, you can use the RNGCryptoServiceProvider class from the System.Security.Cryptography namespace. This class utilizes the cryptographic service provider to generate random numbers with a higher degree of randomness. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> System.Security.Cryptography;
&lt;span style="color:#66d9ef">byte&lt;/span>[] randomBytes = &lt;span style="color:#66d9ef">new&lt;/span> &lt;span style="color:#66d9ef">byte&lt;/span>[&lt;span style="color:#ae81ff">4&lt;/span>];
RNGCryptoServiceProvider rng = &lt;span style="color:#66d9ef">new&lt;/span> RNGCryptoServiceProvider();
rng.GetBytes(randomBytes);
&lt;span style="color:#66d9ef">int&lt;/span> secureRandomNumber = BitConverter.ToInt32(randomBytes, &lt;span style="color:#ae81ff">0&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we generate a 32-bit random number by obtaining four random bytes using GetBytes() method and converting them to an integer using BitConverter.ToInt32().&lt;/p>
&lt;p>Generating random numbers is a common task in many programming scenarios. In C#, you can utilize the Random class for general-purpose random number generation, specifying ranges as needed. For more secure random numbers, the RNGCryptoServiceProvider class is recommended. With these methods at your disposal, you can easily generate random numbers in your C# applications for a wide range of purposes.&lt;/p></description></item><item><title>How To: Move Windows 11 Taskbar/Start Menu Back to the Left</title><link>https://www.darrenhorrocks.co.uk/how-to-move-windows-11-taskbar-back-to-the-left/</link><pubDate>Sun, 30 Oct 2022 20:47:13 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/how-to-move-windows-11-taskbar-back-to-the-left/</guid><description>&lt;p>As we all know, Windows 11 introduced the new centre aligned taskbar. The very first thing that most people wanted to do was move it back to the left, but it does not seem to be immediately obvious how to do so.&lt;/p>
&lt;p>It is actually quite simple when you know how:&lt;/p>
&lt;ol>
&lt;li>Right click on the taskbar&lt;/li>
&lt;li>Click &amp;ldquo;taskbar settings&amp;rdquo;&lt;/li>
&lt;li>Click to expand &amp;ldquo;taskbar behaviours&amp;rdquo;&lt;/li>
&lt;li>Scroll down to &amp;ldquo;taskbar alignment&amp;rdquo;&lt;/li>
&lt;li>Change &amp;ldquo;Centre&amp;rdquo; to &amp;ldquo;Left&amp;rdquo;&lt;/li>
&lt;li>DONE!&lt;/li>
&lt;/ol>
&lt;p>Or watch the youtube video below:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/NciKdQ_C3pI" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div></description></item><item><title>Native pico-sdk ILI9341/ILI9342 display drivere library for Raspberry Pi Pico in C++</title><link>https://www.darrenhorrocks.co.uk/native-pico-sdk-ili9341-ili9342-for-raspberry-pi-pico/</link><pubDate>Mon, 19 Sep 2022 20:08:23 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/native-pico-sdk-ili9341-ili9342-for-raspberry-pi-pico/</guid><description>&lt;p>The ILI9341/ILI9342 works as display driver for a TFT display, and is available as either a standalone IC, or as a breakout board and is usually conntected up via SPI. I have added a &lt;a href="https://github.com/bizzehdee/pico-libs/tree/master/src/common/ili934x">ILI9341 raspberry pi pico library&lt;/a> as part of my group of libraries at the &lt;a href="https://github.com/bizzehdee/pico-libs/">pico-libs&lt;/a> repository on github.&lt;/p>
&lt;p>The library is easy to use and is easily includable in your project via cmake, and libraries are added in, in the same way that you add in the individual libraries from the pico-sdk its self.&lt;/p>
&lt;p>You will first need to check out the pico-libs repository, and then copy external/pico_libs_import.cmake from the repository to the root of your firmware application.&lt;/p>
&lt;p>You can add the library from the pico-lib&amp;rsquo;s adding &lt;code>ili934x&lt;/code> onto the end of your target_link_libraries in cmake:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmake" data-lang="cmake">target_link_libraries(&lt;span style="color:#e6db74">[your&lt;/span> &lt;span style="color:#e6db74">executable]&lt;/span> &lt;span style="color:#e6db74">pico_stdlib&lt;/span> &lt;span style="color:#e6db74">ili934x&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can then include &amp;ldquo;ili934x.h&amp;rdquo; and use the following methods from the &lt;code>ili934x&lt;/code> class.&lt;/p>
&lt;ul>
&lt;li>setRotation(ILI934X_ROTATION rotation = R0DEG)
&lt;ul>
&lt;li>set the rotation of the display&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>setPixel(uint16_t x, uint16_t y, uint16_t colour)
&lt;ul>
&lt;li>set the value of an individual pixel&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>fillRect(uint16_t x, uint16_t y, uint16_t h, uint16_t w, uint16_t colour)
&lt;ul>
&lt;li>fill a rect defined by x, y, h and w&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color)
&lt;ul>
&lt;li>draw a line from any point, to any other point&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>drawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color)
&lt;ul>
&lt;li>draw a circle, specifying the centre and the radius&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>clear(uint16_t colour = COLOUR_BLACK)
&lt;ul>
&lt;li>clear the whole screen&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>blit(uint16_t x, uint16_t y, uint16_t h, uint16_t w, uint16_t *bltBuf)
&lt;ul>
&lt;li>draw a set of RGB data defined by h and w, to the position x and y&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>drawChar(uint16_t x, uint16_t y, char c, uint16_t colour, GFXfont *font)
&lt;ul>
&lt;li>draw a single character on screen&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>charBounds(char c, int16_t *x, int16_t *y, int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy, GFXfont *font)
&lt;ul>
&lt;li>measure the size of an individual character&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>textBounds(const char *str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h, GFXfont *font)
&lt;ul>
&lt;li>measure the size of a string of text&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>All in native C++ with the pico-sdk.&lt;/p></description></item><item><title>Native pico-sdk mpu6050 accelerometer and gyroscope library for Raspberry Pi Pico in C++</title><link>https://www.darrenhorrocks.co.uk/native-pico-sdk-mpu6050-for-raspberry-pi-pico/</link><pubDate>Mon, 19 Sep 2022 20:08:23 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/native-pico-sdk-mpu6050-for-raspberry-pi-pico/</guid><description>&lt;p>The mpu6050 works as an accelerometer and gyroscope, and is available as either a standalone IC, or as a breakout board and is usually conntected up via I2C (IIC, ICC or I&lt;sup>2&lt;/sup>C or SPI, depending on your preference). I have added a &lt;a href="https://github.com/bizzehdee/pico-libs/tree/master/src/common/mpu6050">mpu6050 raspberry pi pico library&lt;/a> as part of my group of libraries at the &lt;a href="https://github.com/bizzehdee/pico-libs/">pico-libs&lt;/a> repository on github.&lt;/p>
&lt;p>The library is easy to use and is easily includable in your project via cmake, and libraries are added in, in the same way that you add in the individual libraries from the pico-sdk its self.&lt;/p>
&lt;p>You will first need to check out the pico-libs repository, and then copy external/pico_libs_import.cmake from the repository to the root of your firmware application.&lt;/p>
&lt;p>You can add the library from the pico-lib&amp;rsquo;s adding &lt;code>mpu6050&lt;/code> onto the end of your target_link_libraries in cmake:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmake" data-lang="cmake">target_link_libraries(&lt;span style="color:#e6db74">[your&lt;/span> &lt;span style="color:#e6db74">executable]&lt;/span> &lt;span style="color:#e6db74">pico_stdlib&lt;/span> &lt;span style="color:#e6db74">mpu6050&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can then include &amp;ldquo;mpu6050.h&amp;rdquo; and use the following methods from the &lt;code>mpu6050&lt;/code> class.&lt;/p>
&lt;ul>
&lt;li>getEvent(sensors_event_t *accel, sensors_event_t *gyro, sensors_event_t *temp)
&lt;ul>
&lt;li>Getting the accelerometer values&lt;/li>
&lt;li>the gyroscope values&lt;/li>
&lt;li>and the temperature values&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>All in native C++ with the pico-sdk.&lt;/p></description></item><item><title>Native pico-sdk BMP280 temperature sensor and air pressure sensor library for Raspberry Pi Pico in C++</title><link>https://www.darrenhorrocks.co.uk/native-pico-sdk-bmp280-for-raspberry-pi-pico/</link><pubDate>Sun, 18 Sep 2022 22:38:46 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/native-pico-sdk-bmp280-for-raspberry-pi-pico/</guid><description>&lt;p>The BMP280 works as an ambient temperature sensor and air pressure sensor, and is available as either a standalone IC, or as a breakout board and is usually conntected up via I2C (IIC, ICC or I&lt;sup>2&lt;/sup>C, depending on your preference). I have written a &lt;a href="https://github.com/bizzehdee/pico-libs/tree/master/src/common/bmp280">bmp280 raspberry pi pico library&lt;/a> as part of a group of libraries over at the &lt;a href="https://github.com/bizzehdee/pico-libs/">pico-libs&lt;/a> repository on github.&lt;/p>
&lt;p>The library is easy to use and is easily includable in your project via cmake, and libraries are added in, in the same way that you add in the individual libraries from the pico-sdk its self.&lt;/p>
&lt;p>You will first need to check out the pico-libs repository, and then copy external/pico_libs_import.cmake from the repository to the root of your firmware application.&lt;/p>
&lt;p>You can add the library from the pico-lib&amp;rsquo;s adding &lt;code>bmp280&lt;/code> onto the end of your target_link_libraries in cmake:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmake" data-lang="cmake">target_link_libraries(&lt;span style="color:#e6db74">[your&lt;/span> &lt;span style="color:#e6db74">executable]&lt;/span> &lt;span style="color:#e6db74">pico_stdlib&lt;/span> &lt;span style="color:#e6db74">bmp280&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can then include &amp;ldquo;bmp280.h&amp;rdquo; and use the following methods from the &lt;code>BMP280&lt;/code> class.&lt;/p>
&lt;ul>
&lt;li>readTemperature();&lt;/li>
&lt;li>readPressure(void);&lt;/li>
&lt;li>readAltitude(float seaLevelhPa = 1013.25);&lt;/li>
&lt;li>seaLevelForAltitude(float altitude, float atmospheric);&lt;/li>
&lt;li>waterBoilingPoint&lt;/li>
&lt;/ul></description></item><item><title>C# Basics - Deep Copy Object Tree</title><link>https://www.darrenhorrocks.co.uk/csharp-basics-deep-copy-object-tree/</link><pubDate>Fri, 01 Jul 2022 11:26:06 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-basics-deep-copy-object-tree/</guid><description>&lt;p>Object deep copy is a process of creating a new object instance that is an exact copy of another object. This process includes creating a new object with all the same properties and values as the original object. Object deep copy is usually used to avoid creating unwanted references between objects, or to create a separate object instance that can be modified without affecting the original object.&lt;/p>
&lt;p>There are several ways to create a deep copy of an object in C#. One way is to use the System.Reflection.MemberInfo.Copy method. This method copies all the public and private members of an object to a new object instance. Another way to create a deep copy is to use the binary serialization mechanism in C#. This approach requires the object to be serialized and deserialized, which can be expensive.&lt;/p>
&lt;p>The System.Object.MemberwiseClone method can also be used to create a deep copy of an object. This method copies all the fields of an object to a new object instance. However, this method does not copy the properties of the object. Therefore, it is not a complete deep copy.&lt;/p>
&lt;p>Here is an example of how to create a deep copy using the System.Reflection.MemberInfo.Copy method:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Person&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Age { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
Person person1 = &lt;span style="color:#66d9ef">new&lt;/span> Person();
person1.Name = &lt;span style="color:#e6db74">&amp;#34;John&amp;#34;&lt;/span>;
person1.Age = &lt;span style="color:#ae81ff">20&lt;/span>;
Person person2 = (Person)person1.Copy();
Console.WriteLine(person2.Name); &lt;span style="color:#75715e">// John
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(person2.Age); &lt;span style="color:#75715e">// 20
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, we first create a Person class with two properties: Name and Age. We then create an instance of the Person class and assign values to the Name and Age properties. Next, we create a deep copy of the person1 object using the Copy method. Finally, we print out the values of the Name and Age properties of the person2 object to the console. As we can see, the values are exactly the same as the original object.&lt;/p>
&lt;p>Here is an example of how to create a deep copy using the binary serialization mechanism:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">using&lt;/span> System;
&lt;span style="color:#66d9ef">using&lt;/span> System.IO;
&lt;span style="color:#66d9ef">using&lt;/span> System.Runtime.Serialization.Formatters.Binary;
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e">[Serializable]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Person&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Age { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
Person person1 = &lt;span style="color:#66d9ef">new&lt;/span> Person();
person1.Name = &lt;span style="color:#e6db74">&amp;#34;John&amp;#34;&lt;/span>;
person1.Age = &lt;span style="color:#ae81ff">20&lt;/span>;
Person person2 = &lt;span style="color:#66d9ef">null&lt;/span>;
&lt;span style="color:#66d9ef">using&lt;/span> (MemoryStream stream = &lt;span style="color:#66d9ef">new&lt;/span> MemoryStream())
{
BinaryFormatter formatter = &lt;span style="color:#66d9ef">new&lt;/span> BinaryFormatter();
formatter.Serialize(stream, person1);
stream.Seek(&lt;span style="color:#ae81ff">0&lt;/span>, SeekOrigin.Begin);
person2 = (Person)formatter.Deserialize(stream);
}
Console.WriteLine(person2.Name); &lt;span style="color:#75715e">// John
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(person2.Age); &lt;span style="color:#75715e">// 20
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, we use the BinaryFormatter class to serialize and deserialize the object. We first serialize the object to a memory stream. We then seek the stream to the beginning and deserialize it to the person2 object. Finally, we print out the values of the Name and Age properties of the person2 object.&lt;/p>
&lt;p>Here is an example of how to create a deep copy using the System.Object.MemberwiseClone method:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Person&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Age { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
Person person1 = &lt;span style="color:#66d9ef">new&lt;/span> Person();
person1.Name = &lt;span style="color:#e6db74">&amp;#34;John&amp;#34;&lt;/span>;
person1.Age = &lt;span style="color:#ae81ff">20&lt;/span>;
Person person2 = (Person)person1.MemberwiseClone();
Console.WriteLine(person2.Name); &lt;span style="color:#75715e">// John
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(person2.Age); &lt;span style="color:#75715e">// 20
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, we use the MemberwiseClone method to create a deep copy of the Person object. This method copies all the fields of the object to a new object. However, it does not copy the properties of the object. Therefore, it is not a complete deep copy.&lt;/p></description></item><item><title>C# Basics: Loop Through a Dictionary</title><link>https://www.darrenhorrocks.co.uk/csharp-basics-loop-through-dictionary/</link><pubDate>Wed, 27 Apr 2022 11:25:28 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-basics-loop-through-dictionary/</guid><description>&lt;p>Dictionaries are essential data structures in C# that allow you to store key-value pairs. Looping through dictionaries enables you to iterate over each key-value pair and perform operations on them. In this article, we will explore different methods of looping through dictionaries in C# and discuss their applications.&lt;/p>
&lt;h2 id="method-1-using-foreach-loop">Method 1: Using foreach Loop&lt;/h2>
&lt;p>The simplest and most common way to iterate through a dictionary is by using a foreach loop. The foreach loop automatically enumerates the key-value pairs of the dictionary. Here&amp;rsquo;s an example&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; dictionary = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;()
{
{ &lt;span style="color:#e6db74">&amp;#34;Apple&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span> },
{ &lt;span style="color:#e6db74">&amp;#34;Banana&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span> },
{ &lt;span style="color:#e6db74">&amp;#34;Orange&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">8&lt;/span> }
};
&lt;span style="color:#66d9ef">foreach&lt;/span> (KeyValuePair&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; kvp &lt;span style="color:#66d9ef">in&lt;/span> dictionary)
{
&lt;span style="color:#66d9ef">string&lt;/span> key = kvp.Key;
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = kvp.Value;
&lt;span style="color:#75715e">// Perform operations with key-value pairs
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Key: &amp;#34;&lt;/span> + key + &lt;span style="color:#e6db74">&amp;#34;, Value: &amp;#34;&lt;/span> + &lt;span style="color:#66d9ef">value&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we have a dictionary containing fruit names as keys and their respective quantities as values. The foreach loop iterates through each key-value pair, allowing you to access the key and value individually for further processing.&lt;/p>
&lt;h2 id="method-2-using-keys-or-values-collection">Method 2: Using Keys or Values Collection&lt;/h2>
&lt;p>If you only need to access either the keys or values of a dictionary, you can use the Keys or Values property to obtain a collection and loop through it. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; dictionary = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;()
{
{ &lt;span style="color:#e6db74">&amp;#34;Apple&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span> },
{ &lt;span style="color:#e6db74">&amp;#34;Banana&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span> },
{ &lt;span style="color:#e6db74">&amp;#34;Orange&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">8&lt;/span> }
};
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span> key &lt;span style="color:#66d9ef">in&lt;/span> dictionary.Keys)
{
&lt;span style="color:#75715e">// Accessing keys only
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Key: &amp;#34;&lt;/span> + key);
}
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> &lt;span style="color:#66d9ef">in&lt;/span> dictionary.Values)
{
&lt;span style="color:#75715e">// Accessing values only
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Value: &amp;#34;&lt;/span> + &lt;span style="color:#66d9ef">value&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we loop through the Keys collection to access and print each key individually. Similarly, we loop through the Values collection to access and print each value separately.&lt;/p>
&lt;h2 id="method-3-using-linq">Method 3: Using LINQ&lt;/h2>
&lt;p>LINQ (Language-Integrated Query) provides a powerful and concise way to query and manipulate data in C#. You can use LINQ to loop through dictionaries and apply various operations. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; dictionary = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;()
{
{ &lt;span style="color:#e6db74">&amp;#34;Apple&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span> },
{ &lt;span style="color:#e6db74">&amp;#34;Banana&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span> },
{ &lt;span style="color:#e6db74">&amp;#34;Orange&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">8&lt;/span> }
};
&lt;span style="color:#66d9ef">var&lt;/span> sortedDictionary = dictionary.OrderBy(x =&amp;gt; x.Key);
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> kvp &lt;span style="color:#66d9ef">in&lt;/span> sortedDictionary)
{
&lt;span style="color:#66d9ef">string&lt;/span> key = kvp.Key;
&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = kvp.Value;
&lt;span style="color:#75715e">// Perform operations with key-value pairs
&lt;/span>&lt;span style="color:#75715e">&lt;/span> Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;Key: &amp;#34;&lt;/span> + key + &lt;span style="color:#e6db74">&amp;#34;, Value: &amp;#34;&lt;/span> + &lt;span style="color:#66d9ef">value&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we use LINQ&amp;rsquo;s OrderBy() method to sort the dictionary by keys in ascending order. Then, we loop through the sorted dictionary and access each key-value pair for further processing.&lt;/p>
&lt;p>Looping through dictionaries in C# allows you to access and manipulate key-value pairs efficiently. The foreach loop is the most common method, providing direct access to both the keys and values. Alternatively, you can loop through the Keys or Values collection if you only need one of them. Additionally, LINQ provides a powerful way to query and manipulate dictionaries. With these methods, you can effectively iterate through dictionaries and perform operations on their contents in your C# applications.&lt;/p></description></item><item><title>C# Basics: What is the difference between 'string' and 'System.String'</title><link>https://www.darrenhorrocks.co.uk/csharp-basics-difference-between-string-system-string/</link><pubDate>Wed, 27 Apr 2022 11:25:13 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-basics-difference-between-string-system-string/</guid><description>&lt;p>In C# &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/string">string&lt;/a> is an alias for &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.string">System.String&lt;/a>, So technically, there is no difference and they are exactly the same as comparing &lt;code>int&lt;/code> and &lt;code>System.Int32&lt;/code>, they will compile to exactly the same code. but&amp;hellip;&lt;/p>
&lt;p>There is a complete list of all aliases to primative types (not all are primitive actually):&lt;/p>
&lt;pre tabindex="0">&lt;code>object: System.Object
string: System.String
bool: System.Boolean
byte: System.Byte
sbyte: System.SByte
short: System.Int16
ushort: System.UInt16
int: System.Int32
uint: System.UInt32
long: System.Int64
ulong: System.UInt64
float: System.Single
double: System.Double
decimal: System.Decimal
char: System.Char
&lt;/code>&lt;/pre>&lt;p>Besides &lt;code>string&lt;/code> and &lt;code>object&lt;/code> all of the aliases are value types. Although decimal is a value type it is not a primitive type in the CLR. System.IntPtr is the only primitive type which doesn&amp;rsquo;t have an alias in C#.&lt;/p>
&lt;p>These value type aliases are known as &amp;ldquo;simple types&amp;rdquo; in the C# spec, and literals can be used for constant values of every simple type.&lt;/p>
&lt;p>With all that said, there is one time where you have to use the aliases: when explicitly specifying an enum&amp;rsquo;s type:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">enum&lt;/span> BadClass : UInt32 {} &lt;span style="color:#75715e">// Invalid
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">enum&lt;/span> GoodClass : &lt;span style="color:#66d9ef">uint&lt;/span> {} &lt;span style="color:#75715e">// Valid
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That&amp;rsquo;s is simply down to the way the spec defines enums - the part after the colon has to be the integral-type, which is one of &lt;code>sbyte&lt;/code>, &lt;code>byte&lt;/code>, &lt;code>short&lt;/code>, &lt;code>ushort&lt;/code>, &lt;code>int&lt;/code>, &lt;code>uint&lt;/code>, &lt;code>long&lt;/code>, &lt;code>ulong&lt;/code>, &lt;code>char&lt;/code>, as opposed to a type as with variable declarations.&lt;/p>
&lt;p>When it comes down to which you should use, it does not matter, any of the aliases or their underlaying types will be compiled down to the same MSIL or binary code.&lt;/p></description></item><item><title>C Sharp 11 Raw String Literals</title><link>https://www.darrenhorrocks.co.uk/c-sharp-11-raw-string-literals/</link><pubDate>Mon, 25 Apr 2022 22:59:26 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/c-sharp-11-raw-string-literals/</guid><description>&lt;p>If you work with literal strings that can contain quotes or language strings such as JSON, XML, HTML, SQL or Regex, &lt;a href="https://github.com/dotnet/csharplang/issues/4304">raw literal strings&lt;/a> may be the best new feature of C# 11.&lt;/p>
&lt;p>If you previously copied a literal string with quotes into a C# literal string, the string ended at the first double quote with compiler errors until you escaped each of the double quotes. If you copied text with curly braces into an interpolated string literal, each curly bracket was interpreted as the beginning of nested code unless you escape each curly bracket by doubling each curly bracket.&lt;/p>
&lt;p>Raw string literals have no escaping. A backslash is output as a backslash, a curly bracket is output as a curly bracket and \n is output as the backslash and an n, not a new line.&lt;/p>
&lt;p>Raw string literals start and end with at least three double quotes &lt;code>(&amp;quot;&amp;quot;&amp;quot;...&amp;quot;&amp;quot;&amp;quot;)&lt;/code>. Within these double quotes, single &lt;code>&amp;quot;&lt;/code> are considered content and included in the string. Any number of double quotes less than the number that opened the raw string literal are treated as content. So, in the common case of three double quotes opening the raw string literals, two double quotes appearing together would just be content. If you need to output a sequence of three or more double quotes, just open and close the raw string literal with at least one more quote than that sequence.&lt;/p>
&lt;p>Raw string literals can be interpolated by preceding them with a &lt;code>$&lt;/code>. The number of &lt;code>$&lt;/code>&amp;rsquo;s that prefix the string is the number of curly brackets that are required to indicate the nested code expression. This means that a &lt;code>$&lt;/code> behaves like the existing string interpolation – a single set of curly brackets indicate nested code. If a raw string literal is prefixed with &lt;code>$$&lt;/code>, a single curly bracket is treated as content and it takes two curly brackets to indicate nested code. Just like with quotes, you can add more $ to allow more curly brackets to be treated as content.&lt;/p>
&lt;p>Read more in the &lt;a href="https://docs.microsoft.com/en-gb/dotnet/csharp/whats-new/csharp-11#raw-string-literals">raw string literals documentation&lt;/a>.&lt;/p></description></item><item><title>Why You Should Use Oauth OAuth 2.0 Tokens</title><link>https://www.darrenhorrocks.co.uk/why-you-should-use-oauth-2-tokens/</link><pubDate>Thu, 14 Apr 2022 21:25:28 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/why-you-should-use-oauth-2-tokens/</guid><description>&lt;p>An OAuth 2.0 access token is usually a base64 encoded string that the client uses when making requests to the server, and can hides the user&amp;rsquo;s identity and other personal information from the client app/website and anybody listening in on the line.&lt;/p>
&lt;p>Access tokens expire after a certain amount of time and become invalid and cannot be used for any API request after the expiration date/time. If offline access is requested to the for the token, you can refresh an access token without prompting the user for permission, even when the user is not present.&lt;/p>
&lt;p>It is best to set the expiration time for refresh token longer than the expiration of the access tokens its self. For example, if you set the expiration for the access token to 30 minutes, set the refresh token&amp;rsquo;s expiration to 24 hours or more (depending on when you expect a user to next use the service).&lt;/p>
&lt;p>Some apps may request that the user reauthenticate after a shorter period of time, which relies on the access token alone rather than a refresh token. These apps have online access as opposed to those that have a refresh token and are considered to have offline access.&lt;/p></description></item><item><title>Observe/React to Array Changes with Angular 12</title><link>https://www.darrenhorrocks.co.uk/observe-array-changes-with-angular-12/</link><pubDate>Mon, 11 Apr 2022 22:37:34 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/observe-array-changes-with-angular-12/</guid><description>&lt;p>In angular 12, it is very simple to have the HTML of your component react to changes in the value of a single variable (or multiple variables).
Although it is not that simple when you want to have your components HTML react to push/pop changes.&lt;/p>
&lt;p>Take the following component:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-typescript" data-lang="typescript">&lt;span style="color:#66d9ef">export&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">TitleComponent&lt;/span> &lt;span style="color:#66d9ef">implements&lt;/span> &lt;span style="color:#a6e22e">OnInit&lt;/span> {
&lt;span style="color:#a6e22e">title&lt;/span>: &lt;span style="color:#66d9ef">string&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;default title&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">constructor&lt;/span>() { }
&lt;span style="color:#a6e22e">ngOnInit&lt;/span>()&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> {
&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">title&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;some title&amp;#34;&lt;/span>
}
&lt;span style="color:#a6e22e">changeTitle&lt;/span>()&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> {
&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">title&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;new title&amp;#34;&lt;/span>;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">&amp;lt;&lt;span style="color:#f92672">div&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">h3&lt;/span>&amp;gt;&amp;lt;&lt;span style="color:#f92672">span&lt;/span>&amp;gt;{{ title }}&amp;lt;/&lt;span style="color:#f92672">span&lt;/span>&amp;gt;&amp;lt;/&lt;span style="color:#f92672">h3&lt;/span>&amp;gt;
&amp;lt;/&lt;span style="color:#f92672">div&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">button&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">(&lt;/span>&lt;span style="color:#a6e22e">click&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">)=&amp;#34;&lt;/span>&lt;span style="color:#a6e22e">changeTitle&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">()&amp;#34;&lt;/span>&amp;gt;CLICK ME!&amp;lt;/&lt;span style="color:#f92672">button&lt;/span>&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you click the &amp;ldquo;change&amp;rdquo; button, the title will change as expected because Angular 12 watches all of your component variables for changes and reflect those changes within the HTML.&lt;/p>
&lt;p>However, if you take the next component, you will find that the same thing does not happen and the HTML does not update, since angular is not able to monitor changes to an array via push/pop.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-typescript" data-lang="typescript">&lt;span style="color:#66d9ef">export&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">StringListComponent&lt;/span> &lt;span style="color:#66d9ef">implements&lt;/span> &lt;span style="color:#a6e22e">OnInit&lt;/span> {
&lt;span style="color:#a6e22e">items&lt;/span>: &lt;span style="color:#66d9ef">string&lt;/span>[] &lt;span style="color:#f92672">=&lt;/span> [];
&lt;span style="color:#66d9ef">constructor&lt;/span>() { }
&lt;span style="color:#a6e22e">ngOnInit&lt;/span>()&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> {
&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">items&lt;/span> &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;first item&amp;#34;&lt;/span>];
}
&lt;span style="color:#a6e22e">addItem&lt;/span>()&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> {
&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">items&lt;/span>.&lt;span style="color:#a6e22e">push&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;new item&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">&amp;lt;&lt;span style="color:#f92672">div&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">*&lt;/span>&lt;span style="color:#a6e22e">ngFor&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;let item of items&amp;#34;&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">div&lt;/span>&amp;gt;&amp;lt;&lt;span style="color:#f92672">span&lt;/span>&amp;gt;{{ item }}&amp;lt;/&lt;span style="color:#f92672">span&lt;/span>&amp;gt;&amp;lt;/&lt;span style="color:#f92672">div&lt;/span>&amp;gt;
&amp;lt;/&lt;span style="color:#f92672">div&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">button&lt;/span> &lt;span style="color:#960050;background-color:#1e0010">(&lt;/span>&lt;span style="color:#a6e22e">click&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">)=&amp;#34;&lt;/span>&lt;span style="color:#a6e22e">addItem&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">()&amp;#34;&lt;/span>&amp;gt;CLICK ME!&amp;lt;/&lt;span style="color:#f92672">button&lt;/span>&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>There is a very simple fix to have angular 12 react to array changes. You simply need to recreate the array for each update as below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-typescript" data-lang="typescript">&lt;span style="color:#66d9ef">export&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">StringListComponent&lt;/span> &lt;span style="color:#66d9ef">implements&lt;/span> &lt;span style="color:#a6e22e">OnInit&lt;/span> {
&lt;span style="color:#a6e22e">items&lt;/span>: &lt;span style="color:#66d9ef">string&lt;/span>[] &lt;span style="color:#f92672">=&lt;/span> [];
&lt;span style="color:#66d9ef">constructor&lt;/span>() { }
&lt;span style="color:#a6e22e">ngOnInit&lt;/span>()&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> {
&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">items&lt;/span> &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;first item&amp;#34;&lt;/span>];
}
&lt;span style="color:#a6e22e">addItem&lt;/span>()&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> {
&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">lists&lt;/span> &lt;span style="color:#f92672">=&lt;/span> [...&lt;span style="color:#66d9ef">this&lt;/span>.&lt;span style="color:#a6e22e">items&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;new item&amp;#34;&lt;/span>];
}
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>uTP Micro Transport Protocol Documented</title><link>https://www.darrenhorrocks.co.uk/utp-micro-transport-protocol-documented/</link><pubDate>Mon, 04 Oct 2021 23:54:05 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/utp-micro-transport-protocol-documented/</guid><description>&lt;p>While updating my &lt;a href="https://github.com/bizzehdee/bzTorrent">bittorrent library&lt;/a>, I found that the uTP (Micro Transport Protocol) documentation is quite lacking, unless you already know how the protocol works, so here is my attempt to expand on the documentation that already exists in an effort to provide more information on implementing uTP.&lt;/p>
&lt;p>Information from &lt;a href="http://www.bittorrent.org/beps/bep_0029.html">BEP 29&lt;/a>, &lt;a href="https://github.com/bittorrent/libutp">bittorrent/libutp&lt;/a> at github and from reverse engineering based on Wireshark UDP dumps.&lt;/p>
&lt;h2 id="notes">Notes&lt;/h2>
&lt;ul>
&lt;li>uTP uses window based congestion control. Each socket has a max_window which determines the maximum number of bytes the socket may have in-flight at any given time. Any packet that has been sent, but not yet acked, is considered to be in-flight&lt;/li>
&lt;/ul>
&lt;h2 id="utp-packet-header-format">uTP Packet Header Format&lt;/h2>
&lt;p>All fields are in network byte order (big endian).&lt;/p>
&lt;pre>&lt;code>0 4 8 16 24 32 (bits)
+-------+-------+---------------+---------------+---------------+
| type | ver | extension | connection_id |
+-------+-------+---------------+---------------+---------------+
| timestamp_microseconds |
+---------------+---------------+---------------+---------------+
| timestamp_difference_microseconds |
+---------------+---------------+---------------+---------------+
| wnd_size |
+---------------+---------------+---------------+---------------+
| seq_nr | ack_nr |
+---------------+---------------+---------------+---------------+
&lt;/code>&lt;/pre>
&lt;h3 id="type">type&lt;/h3>
&lt;p>packet type, and is one of the following 4 bit ints&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Name&lt;/th>
&lt;th>id&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>ST_DATA&lt;/td>
&lt;td>0&lt;/td>
&lt;td>regular data packet, always has a payload&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ST_FIN&lt;/td>
&lt;td>1&lt;/td>
&lt;td>Finalize the connection, is the last packet. seq_nr of this packet will be the last packet expected (though earlier packets may still be in flight)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ST_STATE&lt;/td>
&lt;td>2&lt;/td>
&lt;td>State packet, used to transmit an acknowledgement of a packet, does not increase the seq_nr&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ST_RESET&lt;/td>
&lt;td>3&lt;/td>
&lt;td>Force closes the connection&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ST_SYN&lt;/td>
&lt;td>4&lt;/td>
&lt;td>Connect/Sync packet, seq_nr is set to 1, connection_id is randomised, all subsequent packets sent on this connection are sent with the connection_id + 1&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="version">version&lt;/h3>
&lt;p>protocol version, is always set to 1 and stored as a 4 bit int&lt;/p>
&lt;h3 id="connection_id">connection_id&lt;/h3>
&lt;p>This is a random, unique, 16 bit int, identifying all the packets that belong to the same connection. Initial packet is sent with this number, all subsequent packets are sent with connection_id + 1&lt;/p>
&lt;h3 id="extension">extension&lt;/h3>
&lt;p>not currently used in this library, always set to a 8 bit int of 0&lt;/p>
&lt;h3 id="timestamp_microseconds">timestamp_microseconds&lt;/h3>
&lt;p>The timestamp in microseconds when the packet was sent, this should be as close to the actual transmit time as possible&lt;/p>
&lt;h3 id="timestamp_difference_microseconds">timestamp_difference_microseconds&lt;/h3>
&lt;p>Initialised as 0 for the ST_SYN packet, subsequent packets set this to the current timestamp in microseconds, minus the timestamp_microseconds of the last received packet.&lt;/p>
&lt;h3 id="wnd_size">wnd_size&lt;/h3>
&lt;p>32 bit int specificied in bytes. This is the number of bytes currently sent, but not yet ACK-ed with a ST_STATE. When sending any packets to the remote peer, this must be set to the number of bytes left in the sockets receive buffer.&lt;/p>
&lt;h3 id="seq_nr">seq_nr&lt;/h3>
&lt;p>16 bit int, initialised to 1 or any random positive number. incremented by 1 for each packet sent except for when receiving ST_STATE, serves as an order number for the receiving end.&lt;/p>
&lt;h3 id="ack_nr">ack_nr&lt;/h3>
&lt;p>The seq_nr from the last packet received, unless it is the first ST_SYN, then it is seq_nr - 1.&lt;/p>
&lt;h2 id="congestion-control">Congestion Control&lt;/h2>
&lt;p>Congestion Control uses ledbat congestion control and uses a calculated max_window and also uses wnd_size where max_window is initialised to INT_MAX.&lt;/p>
&lt;pre>&lt;code>const CCONTROL_TARGET = 100 * 1000 // 100ms in microseconds
const MAX_CWND_INCREASE_BYTES_PER_RTT = 3000
const MIN_WINDOW_SIZE = 10
const MAX_WINDOW_DECAY = 100 * 1000
var current_msec = current_microseconds()
var target = CCONTROL_TARGET
if (target &amp;lt;= 0) target = CCONTROL_TARGET
var min_rtt = min(min_rtt, current_msec - last_received_packet.timestamp)
var our_delay = min(min(our_history), min_rtt)
var off_target = target - our_delay
var window_factor = min(bytes_acked, max_window) / max(max_window, bytes_acked)
var delay_factor = off_target / target
var scaled_gain = MAX_CWND_INCREASE_BYTES_PER_RTT * window_factor * delay_factor
if (scaled_gain + max_window &amp;lt; MIN_WINDOW_SIZE) {
max_window = MIN_WINDOW_SIZE
} else {
max_window = max_window + scaled_gain
}
max_window = max_window &amp;lt; MIN_WINDOW_SIZE ? MIN_WINDOW_SIZE : (max_window &amp;gt; socket_send_buff_size ? socket_send_buff_size : max_window) // set new max window
// max_window is now the maximum number of bytes that can be sent with this packet
//can we decay the max_window?
if(current_msec - last_decay_msec &amp;gt;= MAX_WINDOW_DECAY) {
max_window = max_window * 0.5
last_decay_msec = current_msec
if(max_window &amp;lt; MIN_WINDOW_SIZE) {
max_window = MIN_WINDOW_SIZE
}
}
&lt;/code>&lt;/pre>
&lt;h2 id="example-transaction">Example Transaction&lt;/h2>
&lt;ul>
&lt;li>Local peer sends
&lt;ul>
&lt;li>type = ST_SYN&lt;/li>
&lt;li>ver = 1&lt;/li>
&lt;li>extension = 0&lt;/li>
&lt;li>connection_id = 12345 //random number&lt;/li>
&lt;li>timestamp_microseconds = 123456789000&lt;/li>
&lt;li>timestamp_difference_microseconds = 0&lt;/li>
&lt;li>wnd_size = 0&lt;/li>
&lt;li>seq_nr = 1&lt;/li>
&lt;li>ack_nr = 0&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Remote peer receives and decodes the header, and responds with
&lt;ul>
&lt;li>type = ST_STATE&lt;/li>
&lt;li>ver = 1&lt;/li>
&lt;li>extension = 0&lt;/li>
&lt;li>connection_id = 12347 // connection_id from the local peer + 1&lt;/li>
&lt;li>timestamp_microseconds = 133456789000&lt;/li>
&lt;li>timestamp_difference_microseconds = 125000 // difference between when the ST_SYN was sent, and when it was received&lt;/li>
&lt;li>wnd_size = 1000000 // chosen window size&lt;/li>
&lt;li>seq_nr = 1000 // remote peers own sequence number&lt;/li>
&lt;li>ack_nr = 1 // seq_nr from the local peer&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Local peer sends
&lt;ul>
&lt;li>type = ST_DATA&lt;/li>
&lt;li>ver = 1&lt;/li>
&lt;li>extension = 0&lt;/li>
&lt;li>connection_id = 12347 // connection_id from the remote peer&lt;/li>
&lt;li>timestamp_microseconds = 143456789000&lt;/li>
&lt;li>timestamp_difference_microseconds = 125000 // difference between when the ST_STATE was sent, and when it was received&lt;/li>
&lt;li>wnd_size = 1000000 // chosen window size&lt;/li>
&lt;li>seq_nr = 2 // local peers own sequence number&lt;/li>
&lt;li>ack_nr = 1000 // seq_nr from the remote peer&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Remote peer receives and decodes the header, and responds with
&lt;ul>
&lt;li>type = ST_STATE&lt;/li>
&lt;li>ver = 1&lt;/li>
&lt;li>extension = 0&lt;/li>
&lt;li>connection_id = 12347 // connection_id&lt;/li>
&lt;li>timestamp_microseconds = 153456789000&lt;/li>
&lt;li>timestamp_difference_microseconds = 125000 // difference between when the ST_DATA was sent, and when it was received&lt;/li>
&lt;li>wnd_size = 1000000 // chosen window size&lt;/li>
&lt;li>seq_nr = 1000 // remote peers own sequence number&lt;/li>
&lt;li>ack_nr = 2 // seq_nr from the local peer&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul></description></item><item><title>Building a site with Hugo, using GitHub, Amazon S3 and CloudFront with HTTPS and extensionless URLs</title><link>https://www.darrenhorrocks.co.uk/hugo-github-aws-s3-cloudfront-ssl-extensionless/</link><pubDate>Wed, 10 Mar 2021 08:00:00 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/hugo-github-aws-s3-cloudfront-ssl-extensionless/</guid><description>&lt;p>This blog, and many others, are hosted using Amazon AWS. This one has a difference, it is almost completely free to host (and was completely free for the first year) because of Hugo, Amazon S3, Cloudfront, lambda@edge, Azure DevOps and (optionally) GitHub.&lt;/p>
&lt;p>This site is built using &lt;a href="https://gohugo.io/">hugo&lt;/a>, which is a &amp;ldquo;website generator&amp;rdquo; tool that lets you write your pages using markdown, combine that markdown with a theme, and generates a static html website with SEO friendly (optional) extensionless URLs.&lt;/p>
&lt;p>You first need to set your local copy of your hugo website, (&lt;a href="https://gohugo.io/getting-started/quick-start/">use the hugo quick start&lt;/a>) and add some content/posts.&lt;/p>
&lt;p>Now the hugo site is set up, and you have started adding content, we need somewhere to put the site that hugo generates, this is where amazon s3 comes in.&lt;/p>
&lt;p>If you do not already have one, sign up for an amazon aws account (its free to sign up).&lt;/p>
&lt;p>In your aws account, you will need to create an s3 bucket, and the name of the bucket should be the exact domain name you intend to use as your website domain name, the region would be best to be a region close to you, but as long as its on the same continent, it doesnt really matter. Under &amp;ldquo;set permissions&amp;rdquo; you need to uncheck &amp;ldquo;block all public access&amp;rdquo; and uncheck all its child check boxes too. Once created, go to properties &amp;gt; static website hosting and unable &amp;ldquo;use this bucket to host a website&amp;rdquo;, set the index document to index.html and the error document to error.html and save. This should now give you an amazon s3 url to your bucket. If you upload a temporary index.html document, you should now be able to visit the s3 url and see the index page.&lt;/p>
&lt;p>![s3 bucket hosting config][example1]&lt;/p>
&lt;p>To have the website automatically built and deployed to your s3 bucket, you will need to set up a git repository (at github, or azure devops).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmd" data-lang="cmd">&lt;span style="color:#66d9ef">cd&lt;/span> [path to hugo site dir with config.toml]
git init .
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you have done this, create a GitHub repository, and add the GitHub remote repository and push your code to GitHub.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmd" data-lang="cmd">git remote add origin https://github.com/UserName/Repository.git
git push -u origin master
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, you want to create a free &lt;a href="https://azure.microsoft.com/en-us/services/devops/">Azure DevOps&lt;/a> account and link it to GitGub. Next, you will want to add the &amp;ldquo;hugo&amp;rdquo; and the &amp;ldquo;aws toolkit for azure devops&amp;rdquo; extensions to your azure devops account.&lt;/p>
&lt;p>Now, create a new build pipeline in Azure DevOps with the agent specification of &amp;ldquo;windows-2019&amp;rdquo;. Set &amp;ldquo;Get Sources&amp;rdquo; to GitHub and choose the repository that we set up earlier, and choose &amp;ldquo;master&amp;rdquo; as the branch.&lt;/p>
&lt;p>Then you want your build YAML file to look something like this (vmImage needs to be windows as Hugo in devops does not yet support linux images):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-yml" data-lang="yml">&lt;span style="color:#f92672">trigger&lt;/span>:
- &lt;span style="color:#ae81ff">master&lt;/span>
&lt;span style="color:#f92672">pool&lt;/span>:
&lt;span style="color:#f92672">vmImage&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;windows-latest&amp;#39;&lt;/span>
&lt;span style="color:#f92672">steps&lt;/span>:
- &lt;span style="color:#f92672">checkout&lt;/span>: &lt;span style="color:#ae81ff">self&lt;/span>
&lt;span style="color:#f92672">submodules&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;span style="color:#f92672">clean&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
- &lt;span style="color:#f92672">task&lt;/span>: &lt;span style="color:#ae81ff">HugoTask@1&lt;/span>
&lt;span style="color:#f92672">displayName&lt;/span>: &lt;span style="color:#ae81ff">Generate Site&lt;/span>
&lt;span style="color:#f92672">inputs&lt;/span>:
&lt;span style="color:#f92672">source&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;$(Build.SourcesDirectory)&amp;#39;&lt;/span>
&lt;span style="color:#f92672">destination&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;$(Build.ArtifactStagingDirectory)&amp;#39;&lt;/span>
&lt;span style="color:#f92672">baseURL&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;https://mysite.com/&amp;#39;&lt;/span>
- &lt;span style="color:#f92672">task&lt;/span>: &lt;span style="color:#ae81ff">PublishBuildArtifacts@1&lt;/span>
&lt;span style="color:#f92672">inputs&lt;/span>:
&lt;span style="color:#f92672">PathtoPublish&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;$(Build.ArtifactStagingDirectory)&amp;#39;&lt;/span>
&lt;span style="color:#f92672">ArtifactName&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;content&amp;#39;&lt;/span>
&lt;span style="color:#f92672">publishLocation&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;Container&amp;#39;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Save this YAML file and run a build, you should see that your markdown is pulled from github, and then devops generates your site for you, and uploads it to container storage named &amp;ldquo;content&amp;rdquo; within azure devops (be sure that the baseURL matches your baseURL in your hugo config). A new build will be automatically run each time a commit is made to your master branch within github.&lt;/p>
&lt;p>&lt;em>&lt;strong>Option 1: (Hosted directly on s3 with no SSL)&lt;/strong>&lt;/em>&lt;/p>
&lt;p>If you do not want to go through the extra steps of having SSL, and you only want to set up a single website domain, then all you need to do is get your s3 domain from the s3 website setup step, and create a CNAME from the domain (same domain you named the bucket) to the s3 domain.&lt;/p>
&lt;p>i.e. &lt;code>www.mysite.com -&amp;gt; CNAME -&amp;gt; www.mysite.com.s3-website-eu-west-1.amazonaws.com&lt;/code> (depending on your region that you chose).&lt;/p>
&lt;p>Once the new domain has resolved correctly, you are done.&lt;/p>
&lt;p>&lt;em>&lt;strong>Option 2: (Hosted on s3 with cloudfront and SSL and optionally, multiple domains)&lt;/strong>&lt;/em>&lt;/p>
&lt;p>Coming soon :D&lt;/p></description></item><item><title>IMAP4 Client Protocol Library for C# and .NET</title><link>https://www.darrenhorrocks.co.uk/csharp-dotnet-imap-protocol-library/</link><pubDate>Wed, 13 Jan 2021 18:25:21 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-dotnet-imap-protocol-library/</guid><description>&lt;p>&lt;a href="https://github.com/bizzehdee/System.Net.Imap4">System.Net.Imap4&lt;/a> is an IMAP4 client library for .NET written in C# with support for parsing multipart, attachments, html and plain parts.&lt;/p>
&lt;p>It includes:&lt;/p>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Plain and Encrypted (SSL/TLS) Connections&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Fetching server capabilities&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Authentication (Plain only)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Fetching folder lists/Folder selection&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Fetching Email Counts&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Fetching RAW email&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Fetching parsed email&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Deleting emails&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Authentication (Plain only)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Waiting for emails (push emails)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Setting/Getting flags&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Multipart emails&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> HTML emails&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Attachments&lt;/li>
&lt;/ul>
&lt;p>Released under the BSD-3-Clause licence, and is also available for use as an easy to use &lt;a href="https://www.nuget.org/packages/System.Net.Imap4/">nuget package&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Connect to GMail&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> IMapClient ConnectToClient()
{
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Get email&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> EmailMessage GetEmail(&lt;span style="color:#66d9ef">int&lt;/span> id, IMapClient client)
{
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Delete email&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> EmailMessage DeleteEmail(&lt;span style="color:#66d9ef">int&lt;/span> id, IMapClient client)
{
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Get Attachment from email&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> GetAttachments(EmailMessage msg)
{
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>What's New and Interesting in .NET 5</title><link>https://www.darrenhorrocks.co.uk/whats-interesting-in-dotnet-5/</link><pubDate>Thu, 15 Oct 2020 21:52:13 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/whats-interesting-in-dotnet-5/</guid><description>&lt;p>Microsoft recently announced .NET 5 (dotnet5) which is the unification of .NET Framework and dotnet core, leaving behind the divergance, and unifying the whole .NET platform.&lt;/p>
&lt;h2 id="why-net-5-and-not-dotnet-core-40">Why .NET 5 and not dotnet core 4.0&lt;/h2>
&lt;p>Given that Microsoft have already released .NET Framework 4.0 several years ago, they decided the best idea would be to skip the 4.0 release with dotnet core to avoid confusing users (which they did for sort of the same reason in Windows 10), and they landed on .NET 5.0, unifying the names and the numbers from Framework and core.&lt;/p>
&lt;h2 id="the-unified-platform">The Unified Platform&lt;/h2>
&lt;p>.NET 5 will be able to target Windows, Linux, macOS, iOS, Android, tvOS, watchOS and WebAssembly. Building on top of dotnet core and the best of mono to create a platform that will allow developers to create apps for multiple operating systems and devices from a single codebase and a single framework.&lt;/p>
&lt;h2 id="runtime">Runtime&lt;/h2>
&lt;p>&lt;strong>CoreCLR&lt;/strong> is the runtime that is used in dotnet core, and has been targeted at cloud and web applications, and more recently, desktop applications and IoT devices.&lt;/p>
&lt;p>&lt;strong>Mono&lt;/strong> is the original cross platform runtime that was designed to be compatible with .NET. It started out as an alternitive, but was later brought into the fold of the mainstream platform and is at the core of Xamarin.&lt;/p>
&lt;p>With .NET 5 and .NET 6 in the future, Microsoft aims to have CoreCLR and Mono be fully interchangable with each other, i.e. a .NET application can run the same on either runtime.&lt;/p>
&lt;h2 id="ryujit-the-assembly-code-generator">RyuJIT (the assembly code generator)&lt;/h2>
&lt;p>There are many enhancements in RyuJIT, such as enabling the &lt;a href="https://github.com/dotnet/runtime/pull/36263">suppression of some bounds checks&lt;/a>, &lt;a href="https://github.com/dotnet/runtime/pull/37038">tail duplication improvements&lt;/a> and &lt;a href="https://github.com/dotnet/runtime/pull/37786">removal of redundant zero inits&lt;/a>. RyuJIT also implements &lt;a href="https://github.com/dotnet/runtime/issues/33308">ARM64 hardware intrinsics&lt;/a>, creating smaller and faster code when building for AMD64 targets.&lt;/p>
&lt;h2 id="garbage-collection">Garbage collection&lt;/h2>
&lt;p>Garbage collection will now expose detailed data on the most recent collection, developers can use the &lt;code>GetGCMemoryInfo&lt;/code> method, which will return a &lt;code>GCMemoryInfo&lt;/code> object. The &lt;code>GCMemoryInfo&lt;/code> object contains information about machine memory, heap memory and the most recent collection, or the most recent specified collection such as: ephemeral, full blocking, or background. The main use for this would be logging/monitoring and to communicate to a load balancer that a machine should be taken out of rotation.&lt;/p>
&lt;h2 id="systemtextjson">System.Text.Json&lt;/h2>
&lt;p>The new System.Text.Json allows the user the ability to ignore default values for value type properties when serializing, reducing resource costs (both CPU and network), this is a breaking change for System.Text.Json, but is a welcome change. System.Text.Json also has ways of dealing with circular references when serializing.&lt;/p>
&lt;h2 id="what-else">What else?&lt;/h2>
&lt;p>When .NET 5 hits production release in november 2020, we expect to see cross platform support (full or partial) for:&lt;/p>
&lt;ul>
&lt;li>ASP.NET Core, an open source framework for web applications.&lt;/li>
&lt;li>Entity Framework Core data access technology.&lt;/li>
&lt;li>WinForms.&lt;/li>
&lt;li>WPF (Windows Presentation Foundation).&lt;/li>
&lt;li>Xamarin mobile app device model.&lt;/li>
&lt;li>ML.NET.&lt;/li>
&lt;/ul>
&lt;p>As well as:&lt;/p>
&lt;ul>
&lt;li>C# 9 and F# 5 language support&lt;/li>
&lt;li>Improved HTTP 1.1 and HTTP 2 performance&lt;/li>
&lt;li>Single file applications (linking all assemblies into a single exe)&lt;/li>
&lt;/ul>
&lt;p>Take a look at the &lt;a href="https://dotnet.microsoft.com/">microsoft dotnet&lt;/a> website for downloads and more information.&lt;/p></description></item><item><title>C# Console Async Main - Async Entry Point for Console Apps</title><link>https://www.darrenhorrocks.co.uk/csharp-console-async-main-async-to-the-core/</link><pubDate>Wed, 14 Oct 2020 09:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-console-async-main-async-to-the-core/</guid><description>&lt;p>As of C# 7.1, it is possible with console apps, to have async all the way to the entry point. The previous constraints of the entry point have been the same all the way up to this point, and similar to the entry point in C/C++ apps. The C# entry point method must be &lt;code>static&lt;/code>, the name of the method must be &lt;code>Main&lt;/code>, the return type must be either void or int and finally, the method can accept zero arguments, or exactly one argument of &lt;code>string[]&lt;/code>, which contains the command arguments.&lt;/p>
&lt;p>Given those limitations, the 4 possible entry points before C# 7.1 are:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main();
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Main();
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If we wanted to mimic async all the way to the core, we would have to do something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">return&lt;/span> MainAsync(args).GetAwaiter().GetResult();
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; MainAsync(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#75715e">//do async main
&lt;/span>&lt;span style="color:#75715e">&lt;/span>}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>While this works, it looks dirty, and is an extra step we shouldnt need to be taking.&lt;/p>
&lt;h2 id="c-71">C# 7.1&lt;/h2>
&lt;p>Now that we have C# 7.1, we get these new possible entry points for a console app. You also have the option of having the &lt;code>async&lt;/code> keyword on each of these new entry points.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Task Main();
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; Main();
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Task Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>With C# 7.1, this makes async Main much easier, and much simpler and much better, and allows us to write apps that are console based that are async all the way down to the entry point, making for much more scope to create console apps without deadlocks, and without waiting for tasks that are CPU bound.&lt;/p></description></item><item><title>Problems With Copying a Stack&lt;T> in C# and How to Fix It</title><link>https://www.darrenhorrocks.co.uk/csharp-problems-with-copying-a-stack-and-how-to-fix/</link><pubDate>Tue, 13 Oct 2020 09:35:53 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-problems-with-copying-a-stack-and-how-to-fix/</guid><description>&lt;p>In C# and in .NET in general, there is a generic stack class which accepts a single type parameter. The stack class is a FILO (first in last out) collection, and can be liked to stacking plates, you cannot remove the bottom plate, without first removing all the plates above it. The problem with the &lt;code>Stack&amp;lt;T&amp;gt;&lt;/code> class is that it has an &lt;code>IEnumerable&amp;lt;T&amp;gt;&lt;/code> constructor&amp;hellip;&lt;/p>
&lt;p>This constructor suggests that whatever collection is passed to it, is copied exactly to the new stack. The problem is, it is both true and not true at the same time, especially if you are copying another stack.&lt;/p>
&lt;p>If you pass a stack to another stack and expect to have the same stack twice, you will start to run into problems.&lt;/p>
&lt;p>If we init a stack like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> firstStack = &lt;span style="color:#66d9ef">new&lt;/span> Stack&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;();
firstStack.Push(&lt;span style="color:#ae81ff">1&lt;/span>);
firstStack.Push(&lt;span style="color:#ae81ff">2&lt;/span>);
firstStack.Push(&lt;span style="color:#ae81ff">3&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You will end up with 1 at the bottom of the stack, 2 on top of 1 and then 3 at the top of the stack.&lt;/p>
&lt;p>If you then want to copy that stack to another stack, you would assume this is correct:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">var&lt;/span> secondStack = &lt;span style="color:#66d9ef">new&lt;/span> Stack&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;(firstStack);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You would assume that &lt;code>secondStack&lt;/code> would also end up with 1 at the bottom of the stack, 2 on top of 1, and 3 at the top of the stack, but this is wrong. What actually happens is the reverse of this. 3 ends up at the bottom of the stack, 2 in the middle and 1 at the top.&lt;/p>
&lt;p>The reason for this is that in converting the &lt;code>Stack&amp;lt;T&amp;gt;&lt;/code> to an &lt;code>IEnumerable&amp;lt;T&amp;gt;&lt;/code>, what essentially happens is, the old stack is itterated from top to bottom, rather than bottom to top. This has the effect of the last item added to the first stack, becoming the first item that is added to the new stack, essentially reversing the stack in the copy.&lt;/p>
&lt;p>To get around this, the most simple option when copying a stack, is to double copy the stack (though with large stacks, this is not the most efficient), and an extension method for doing this can be as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> Stack&amp;lt;T&amp;gt; Clone&amp;lt;T&amp;gt;(&lt;span style="color:#66d9ef">this&lt;/span> Stack&amp;lt;T&amp;gt; originalStack)
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> Stack&amp;lt;T&amp;gt;(&lt;span style="color:#66d9ef">new&lt;/span> Stack&amp;lt;T&amp;gt;(originalStack));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This has the effect of reversing the stack, and then reversing it again in a second copy. The inner stack and its items will be released by the GC very quickly, and we now have a method which we can use to clone a &lt;code>Stack&amp;lt;T&amp;gt;&lt;/code>.&lt;/p></description></item><item><title>Simple C++ Neural Network Library</title><link>https://www.darrenhorrocks.co.uk/simple-cplusplus-neural-network-library/</link><pubDate>Sun, 11 Oct 2020 09:58:35 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/simple-cplusplus-neural-network-library/</guid><description>&lt;p>The C++ library (&lt;a href="https://github.com/bizzehdee/neuro">source on github&lt;/a>) is designed to be a core neural network library, implementing basic neurons, layers and networks and comes with some basic learning methods too. The library is complete enough for those who want to learn and understand neural networks and how they are put together, but by no means is intended to be a complete AI library.&lt;/p>
&lt;p>(Originally based On &lt;a href="http://www.codeproject.com/Articles/16447/Neural-Networks-on-C">Neural Networks on C#&lt;/a>)&lt;/p>
&lt;h2 id="main-classes">Main Classes&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Neuron&lt;/strong> - An abstract base class for all neurons. Encapsulates entities like a neuron&amp;rsquo;s weight, output value, and input value. Other neuron classes inherit from the base class.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Layer&lt;/strong> - A collection of neurons. This is a base abstract class, which encapsulates common functionality for all neuron&amp;rsquo;s layers.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Network&lt;/strong> - A collection of layers. This is an abstract base class for a layered neural network.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>IActivationFunction&lt;/strong> - Pure virtual interface for activation functions. Activation functions are used in activation neurons - the type of neuron, where the weighted sum of its inputs is calculated and then the value is passed as input to the activation function, and the output value becomes the output value of the neuron.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>IUnsupervisedLearning&lt;/strong> - Pure virtual interface for an unsupervised learning algorithms - the type of learning algorithms where a system is provided with sample inputs only during the learning phase, but not with the desired outputs.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>ISupervisedLearning&lt;/strong> - Pure virtual interface for a supervised learning algorithms - this type of learning algorithms where a system is provided with sample inputs, with desired output values during the learning phase.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="architecture">Architecture&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Activation Network&lt;/strong> - The neural network where each neuron computes its output as the activation function&amp;rsquo;s output, and the argument is a weighted sum of its inputs combined with the threshold value. The network may consist of a single layer, or of multiple layers. Trained with supervised learning algorithms, the network allows to solve such tasks as approximation, prediction, classification, and recognition.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Distance Network&lt;/strong> - The neural network where each neuron computes its output as a distance between its weight values and input values. The network consists of a single layer, and may be used as a base for such networks like Kohonen Self Organizing Map, Elastic Network, and Hamming Network.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="learning">Learning&lt;/h2>
&lt;p>Different learning algorithms are used to train different neural networks, and are used to solve different problems:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Perceptron">Perceptron Learning&lt;/a> - the algorithm may be considered as the first neural network learning algorithm, and its history starts from 1957. The algorithm may be used with a one-layer activation network, where each neuron has a threshold activation function. The range of its applications are rather small and limited the with classification of linearly separable data.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Delta_rule">Delta Rule Learning&lt;/a> - the algorithm is a next step after the perceptron learning algorithm. It utilizes the activation function&amp;rsquo;s derivative, and may be applicable to single-layer activation networks only, where each neuron has a continuous activation function instead of a threshold activation function. The most popular continuous activation function is the unipolar and bipolar sigmoid function. Because the algorithm may be applied to one-layer networks only, it is limited to some classification and recognition tasks mostly.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Backpropagation">Back Propagation Learning&lt;/a> - this is one of the most popular and known algorithms for multi-layer neural network learning. Initially, it was described in 1974, and from that time, it was extensively studied and applied to a broad range of different tasks. Because the algorithm is able to train multi-layer neural networks, the range of its applications is very great, and includes such tasks as approximation, prediction, object recognition, etc.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Self-organizing_map">SOM Learning&lt;/a> - this algorithm was developed by Kohonen, and may be considered as one of the most famous unsupervised learning algorithms for clusterization problems. It treats neural network as a 2D map of nodes, where each node may represent a separate class. The algorithm organizes a network in such a way, that it becomes possible to find the correlation and similarities between data samples.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Elastic Network Learning - the algorithm is similar to the idea of the SOM learning algorithm, but it treats network neurons not as a 2D map of nodes, but as a ring. During the learning procedure, the ring gets some shape, which represents a solution. One of the most common demonstrations of this learning algorithm is the Traveling Salesman Problem (TSP).&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>dotnet BitTorrent library written in C#</title><link>https://www.darrenhorrocks.co.uk/csharp-dotnet-bittorrent-library-scraper-client/</link><pubDate>Fri, 09 Oct 2020 23:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/csharp-dotnet-bittorrent-library-scraper-client/</guid><description>&lt;p>&lt;a href="https://github.com/bizzehdee/System.Net.Torrent">System.Net.Torrent&lt;/a> is an open source bittorrent scraper and peer wire implementation written in C#&lt;/p>
&lt;p>It includes:&lt;/p>
&lt;ul>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Tracker Announce (HTTP/UDP)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Tracker Scrape (HTTP/UDP)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> PeerWire (TCP) Client&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Choke&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Unchoke&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Interested&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Not Interested&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Have&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Bitfield (Optional obsfucation)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Request&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Piece&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Fast Protocol Extensions&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Extended Protocol Extensions&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Local Peer Discovery (Non-existant Multicast BEP-14)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Peer Exchange (utPEX)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Trackerless Metadata (utMetadata)&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Magnet Link Parser&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Bencode Encode/Decode&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> .torrent Metadata Parser&lt;/li>
&lt;li>&lt;input checked="" disabled="" type="checkbox"> Tracker Exchange Protocol&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Resolving a magnet link to metadata (.torrent file)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> TestAsyncMagnetLink()
{
&lt;span style="color:#66d9ef">var&lt;/span> ubuntuMagnetLink = &lt;span style="color:#e6db74">&amp;#34;magnet:?xt=urn:btih:e4be9e4db876e3e3179778b03e906297be5c8dbe&amp;amp;dn=ubuntu-18.04-desktop-amd64.iso&amp;amp;tr=http://torrent.ubuntu.com:6969/announce&amp;#34;&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> magnetMetadata = MagnetLink.ResolveToMetadata(ubuntuMagnetLink);
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> item &lt;span style="color:#66d9ef">in&lt;/span> magnetMetadata.AnnounceList)
{
Console.WriteLine(item);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Scraping announce/tracker URLs&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> ScrapeTorrent()
{
&lt;span style="color:#66d9ef">var&lt;/span> scraper = &lt;span style="color:#66d9ef">new&lt;/span> HTTPTrackerClient(&lt;span style="color:#ae81ff">15&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> announce = scraper.Scrape(&lt;span style="color:#e6db74">&amp;#34;http://torrent.ubuntu.com:6969/announce&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">new&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span>[] { &lt;span style="color:#e6db74">&amp;#34;e4be9e4db876e3e3179778b03e906297be5c8dbe&amp;#34;&lt;/span> });
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Announce (request peers from the tracker)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> AnnounceTorrent()
{
&lt;span style="color:#66d9ef">var&lt;/span> scraper = &lt;span style="color:#66d9ef">new&lt;/span> HTTPTrackerClient(&lt;span style="color:#ae81ff">15&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> peers = scraper.Announce(&lt;span style="color:#e6db74">&amp;#34;http://torrent.ubuntu.com:6969/announce&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;e4be9e4db876e3e3179778b03e906297be5c8dbe&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;-LW2222-011345223110&amp;#34;&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Creating Windows and Linux background services in dotnet core 3.0/3.1</title><link>https://www.darrenhorrocks.co.uk/dotnet-core-3-background-worker-services/</link><pubDate>Thu, 30 Apr 2020 19:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/dotnet-core-3-background-worker-services/</guid><description>&lt;p>There are times when you need an application to just sit there in the background and deal with the same repetitive task over and over again.&lt;/p>
&lt;p>In dotnet core 3.0, a new type of template was introduced, &amp;ldquo;Worker Service&amp;rdquo;, which is a very easy way of creating Windows Services and Linux Daemons.&lt;/p>
&lt;h2 id="the-basics">The Basics&lt;/h2>
&lt;p>The new &amp;ldquo;worker&amp;rdquo; template can be accessed in one of two ways:&lt;/p>
&lt;ol>
&lt;li>via the command line&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmd" data-lang="cmd">&lt;span style="color:#66d9ef">mkdir&lt;/span> myNewWorker
&lt;span style="color:#66d9ef">cd&lt;/span> myNewWorker
dotnet new worker
&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>via Visual Studio&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="/images/dotnet-new-worker-figure-1.png" alt="Visual Studio new background worker project example" title="Visual Studio new background worker project example">&lt;/p>
&lt;p>Either way, you end up with the same basic template to start with, which is your Startup class, and your Worker class.&lt;/p>
&lt;h2 id="the-startup-class">The Startup Class&lt;/h2>
&lt;p>The startup class is your entry point to the executable
In the initial template, we are&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> IHostBuilder CreateHostBuilder(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">return&lt;/span> Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =&amp;gt;
{
services.AddHostedService&amp;lt;Worker&amp;gt;();
});
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first thing we need to do is add the generic host library nuget package &lt;code>Microsoft.Extensions.Hosting&lt;/code> and switch the SDK to the worker SDK.&lt;/p>
&lt;p>You can do this by opening up your csproj file and making the following changes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk.Worker&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>netcoreapp3.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PackageReference&lt;/span> &lt;span style="color:#a6e22e">Include=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.Extensions.Hosting&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, we need to add the following lines to our &lt;code>CreateHostBuilder&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> IHostBuilder CreateHostBuilder(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">return&lt;/span> Host.CreateDefaultBuilder(args)
.UseSystemd()
.UseWindowsService()
.ConfigureServices((hostContext, services) =&amp;gt;
{
services.AddHostedService&amp;lt;Worker&amp;gt;();
});
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-worker-class">The Worker class&lt;/h2>
&lt;p>Lastly, we can create our background service worker class, which is executed asynchronously, has logging, and has configuration built in too.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp"> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Worker&lt;/span> : BackgroundService
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> ILogger&amp;lt;Worker&amp;gt; &lt;span style="color:#ae81ff">_l&lt;/span>ogger;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> IConfiguration configuration;
&lt;span style="color:#66d9ef">public&lt;/span> Worker(ILogger&amp;lt;Worker&amp;gt; logger, IConfiguration configuration)
{
&lt;span style="color:#ae81ff">_l&lt;/span>ogger = logger;
&lt;span style="color:#66d9ef">this&lt;/span>.configuration = configuration;
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task ExecuteAsync(CancellationToken stoppingToken)
{
&lt;span style="color:#66d9ef">while&lt;/span> (!stoppingToken.IsCancellationRequested)
{
&lt;span style="color:#66d9ef">await&lt;/span> Task.Delay(&lt;span style="color:#ae81ff">1000&lt;/span>, stoppingToken);
}
}
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>dotnet Core and C# Dependency Injection</title><link>https://www.darrenhorrocks.co.uk/dotnet-core-and-c-sharp-dependency-injection/</link><pubDate>Fri, 24 Apr 2020 11:00:00 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/dotnet-core-and-c-sharp-dependency-injection/</guid><description>&lt;p>This is a short introduction into Dependency Injection for those who are new to dotnet core and C#, with an attempt to explain what it is, and how it works.&lt;/p>
&lt;h2 id="so-what-is-dependency-injection">So, what is Dependency Injection?&lt;/h2>
&lt;p>DI is a programming method that makes each class independent of the other classes around it. Meaning that we don&amp;rsquo;t rely on our dependencies working exactly how we expect, and only rely on the API being there to use.&lt;/p>
&lt;h2 id="whats-the-point">What&amp;rsquo;s the point?&lt;/h2>
&lt;p>The two main advantages are:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Flexibility&lt;/strong>: The implementation of each dependency can be switched for any other implementation, as only the interface is required by each class.&lt;/li>
&lt;li>&lt;strong>Testability&lt;/strong>: Relying only on interfaces allows us to test individual classes on their own without relying on any of the dependencies, making testing simpler and more straight forward.&lt;/li>
&lt;/ul>
&lt;h2 id="how-to-use-it-in-dotnet-core">How to use it in dotnet core?&lt;/h2>
&lt;p>Fortunately, dotnet core has a built in Dependency Injection engine (referred to as DI, or IoC or Inversion of Control), in fact, it is used by many of the internal dotnet core services and frameworks. The best thing, is that it all exists within &lt;code>Microsoft.Extensions.DependencyInjection&lt;/code>, which can be added via nuget if you do not already have it in your project.&lt;/p>
&lt;h2 id="how-do-we-know-what-is-and-is-not-a-service-we-should-register">How do we know what is and is not a service we should register?&lt;/h2>
&lt;p>It is quite simple, and there are a few &amp;ldquo;rules&amp;rdquo; to help you figure it out:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Lifespan within the scope&lt;/strong>: Are we instantiating the service in the scope of the method?&lt;/li>
&lt;li>&lt;strong>Multiple versions&lt;/strong>, Is there be more than one (possible) version of this service?&lt;/li>
&lt;li>&lt;strong>Testing&lt;/strong>, really, you only want to test one specific method at a time. If you have code that does many other things, you should to move that to a dedicated service. This moved code would then become a dependency its self.&lt;/li>
&lt;li>&lt;strong>Output&lt;/strong>, i.e accessing a network resource, doing an API call or interacting with I/O - then it should be placed in a separate service and be injected in as a dependency.&lt;/li>
&lt;/ul>
&lt;h2 id="example">Example&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> CompletePurchase(&lt;span style="color:#66d9ef">double&lt;/span> amount, &lt;span style="color:#66d9ef">string&lt;/span> cardNumber, &lt;span style="color:#66d9ef">string&lt;/span> address, &lt;span style="color:#66d9ef">string&lt;/span> city, &lt;span style="color:#66d9ef">string&lt;/span> name)
{
&lt;span style="color:#66d9ef">var&lt;/span> paymentService = &lt;span style="color:#66d9ef">new&lt;/span> PaymentService();
&lt;span style="color:#66d9ef">var&lt;/span> successfullyCharged = paymentService.Charge(&lt;span style="color:#66d9ef">int&lt;/span> amount, cardNumber);
&lt;span style="color:#66d9ef">if&lt;/span> (successfullyCharged)
{
&lt;span style="color:#66d9ef">var&lt;/span> shippingService = &lt;span style="color:#66d9ef">new&lt;/span> ShippingService();
shippingService.Ship(address, city, name);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The problems with the above code are:&lt;/p>
&lt;ul>
&lt;li>The first problem is that we directly instantiate &lt;code>PaymentService&lt;/code> and &lt;code>ShippingService&lt;/code>, so we risk triggering a HTTP call when trying to test&lt;/li>
&lt;li>We would have to include test code for &lt;code>PaymentService&lt;/code> and &lt;code>ShippingService&lt;/code>, or all code paths would not be fully tested&lt;/li>
&lt;li>We do not control this code due to the external code paths&lt;/li>
&lt;/ul>
&lt;p>What we should be doing is injecting the services via the class constructor, allowing us to swap out the implementations at will, with new implementations or with mock implementations.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Checkout&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> IPaymentService &lt;span style="color:#ae81ff">_&lt;/span>paymentService;
&lt;span style="color:#66d9ef">private&lt;/span> IShippingService &lt;span style="color:#ae81ff">_&lt;/span>shippingService;
&lt;span style="color:#66d9ef">public&lt;/span> Checkout(IPaymentService paymentService, IShippingService shippingService)
{
&lt;span style="color:#ae81ff">_&lt;/span>paymentService = paymentService;
&lt;span style="color:#ae81ff">_&lt;/span>shippingService = shippingService;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> CompletePurchase(&lt;span style="color:#66d9ef">double&lt;/span> amount, &lt;span style="color:#66d9ef">string&lt;/span> cardNumber, &lt;span style="color:#66d9ef">string&lt;/span> address, &lt;span style="color:#66d9ef">string&lt;/span> city, &lt;span style="color:#66d9ef">string&lt;/span> name)
{
&lt;span style="color:#66d9ef">var&lt;/span> successfullyCharged = &lt;span style="color:#ae81ff">_&lt;/span>paymentService.Charge(&lt;span style="color:#66d9ef">int&lt;/span> amount, cardNumber);
&lt;span style="color:#66d9ef">if&lt;/span> (successfullyCharged)
{
&lt;span style="color:#ae81ff">_&lt;/span>shippingService.Ship(address, city, name);
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This code now:&lt;/p>
&lt;ul>
&lt;li>Is only concerned about one thing, converting a charge into a shipment (where the actual charge, and shipping are performed by another service)&lt;/li>
&lt;li>We control the entirity of this code&lt;/li>
&lt;li>We can swap out different implementations of &lt;code>IPaymentService&lt;/code> and &lt;code>IShippingService&lt;/code> allowing us to create mock implementations so that we can test just &lt;code>CompletePurchase&lt;/code> in isolation.&lt;/li>
&lt;/ul>
&lt;h2 id="enter-dotnet-core">Enter dotnet core&lt;/h2>
&lt;p>The built-in container is represented by an &lt;code>IServiceProvider&lt;/code> implementation that supports constructor injection by default. The classes managed by the built-in IoC container are called services.&lt;/p>
&lt;p>ASP.NET Core allows us to register our application services with IoC container, in the &lt;code>ConfigureServices&lt;/code> method of the &lt;code>Startup&lt;/code> class. The &lt;code>ConfigureServices&lt;/code> method includes a parameter of the &lt;code>IServiceCollection&lt;/code> interface which is used to register application services&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Startup&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> ConfigureServices(IServiceCollection services)
{
services.Add(&lt;span style="color:#66d9ef">new&lt;/span> ServiceDescriptor(&lt;span style="color:#66d9ef">typeof&lt;/span>(IPaymentService), &lt;span style="color:#66d9ef">new&lt;/span> PaymentService()));
services.Add(&lt;span style="color:#66d9ef">new&lt;/span> ServiceDescriptor(&lt;span style="color:#66d9ef">typeof&lt;/span>(IShippingService), &lt;span style="color:#66d9ef">new&lt;/span> ShippingService()));
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>So IoC and DI in ASP.NET Core and dotnet core in general is very easy, and more or less built in.&lt;/p></description></item><item><title>dotnet core 3.0 Self Contained Single Executable (and other build optimisations)</title><link>https://www.darrenhorrocks.co.uk/dotnet-core-3-self-contained-single-executable/</link><pubDate>Tue, 21 Apr 2020 23:36:31 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/dotnet-core-3-self-contained-single-executable/</guid><description>&lt;p>Applications feel more optimised when their binaries, or set of binaries are small. With dotnet core 3.0 there are some features built in that help facilitate this.&lt;/p>
&lt;h2 id="specify-the-build-target">Specify the build target&lt;/h2>
&lt;p>First of all, you should specify your build target for your release builds, this will create a binary specifically optimised and targeted for that platform.&lt;/p>
&lt;p>A non-exhaustive list of possible runtime targets are: &lt;em>win-x64, win-x86, win-arm, win-arm64, linux-x64, linux-arm, osx-x64&lt;/em>. (The full list can be found on the microsoft &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/rid-catalog">runtime id catalog&lt;/a>)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cmd" data-lang="cmd">dotnet publish -r win-x64 -c release
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="publish-trimmed">Publish Trimmed&lt;/h2>
&lt;p>Publishing in trimmed mode, the dotnet core compiler attempts to work out which assemblies will actually be used, and which will not, and removes all unused assemblies from the published output.&lt;/p>
&lt;p>To do this, you will need to add a single xml block to your csproj file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>netcoreapp3.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishTrimmed&amp;gt;&lt;/span>true&lt;span style="color:#f92672">&amp;lt;/PublishTrimmed&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Though if your app or library uses reflection in any sort of way, you need to let the tool-chain know that you do not want certain assemblies trimmed (because dotnet is good, but its not THAT good). You would do this by adding a block of XML like the following to your csproj file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TrimmerRootAssembly&lt;/span> &lt;span style="color:#a6e22e">Include=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;System.Data.SqlClient&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="publish-as-single-file">Publish as Single File&lt;/h2>
&lt;p>Another new option that comes with dotnet core 3.0 is the ability to publish your entire application as a single assembly (exe/dll).&lt;/p>
&lt;p>To do this, you will need to the option to your csproj file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>netcoreapp3.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishSingleFile&amp;gt;&lt;/span>true&lt;span style="color:#f92672">&amp;lt;/PublishSingleFile&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This can be combined with &lt;code>PublishTrimmed&lt;/code> to produce a very small assemby that contains your application and the required assemblies required to execute the application in a single exe/dll without having to ship the entire dotnet core 3.0 runtime.&lt;/p>
&lt;h2 id="publish-as-ready-to-run-aot">Publish as Ready to Run (AOT)&lt;/h2>
&lt;p>ReadyToRun is by far the most exciting new option made available in dotnet core 3.0. This isnt simply JITing the MSIL, this is a genuine compilation of as much MSIL to native binary as possible. This is an incredible optimisation if you plan to publish packages on a per-platform basis. This is all as simple as adding the option to your csproj file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>netcoreapp3.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishReadyToRun&amp;gt;&lt;/span>true&lt;span style="color:#f92672">&amp;lt;/PublishReadyToRun&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you do have any assemblies that you would like to remain as MSIL in your project for whatever reason (incompatabilities or if the R2R compiler fails) you can exclude any assemblies with the following&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishReadyToRunExclude&lt;/span> &lt;span style="color:#a6e22e">Include=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;AssemblyToExclude.dll&amp;#34;&lt;/span> &lt;span style="color:#f92672">/&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="combined">Combined&lt;/h2>
&lt;p>For the best results, it is possible to combine all these options&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-xml" data-lang="xml">&lt;span style="color:#f92672">&amp;lt;Project&lt;/span> &lt;span style="color:#a6e22e">Sdk=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Microsoft.NET.Sdk&amp;#34;&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;TargetFramework&amp;gt;&lt;/span>netcoreapp3.0&lt;span style="color:#f92672">&amp;lt;/TargetFramework&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishTrimmed&amp;gt;&lt;/span>true&lt;span style="color:#f92672">&amp;lt;/PublishTrimmed&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishSingleFile&amp;gt;&lt;/span>true&lt;span style="color:#f92672">&amp;lt;/PublishSingleFile&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;PublishReadyToRun&amp;gt;&lt;/span>true&lt;span style="color:#f92672">&amp;lt;/PublishReadyToRun&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/PropertyGroup&amp;gt;&lt;/span>
&lt;span style="color:#f92672">&amp;lt;/Project&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Adding appsettings.json options to a dotnet core console application</title><link>https://www.darrenhorrocks.co.uk/dotnet-core-console-appsettings-json/</link><pubDate>Sun, 17 Nov 2019 10:08:04 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/dotnet-core-console-appsettings-json/</guid><description>&lt;p>Something that annoyingly does not come as standard in the dotnet core console application template, and, it is not documented anywhere with Microsoft is: appsettings.json.
This comes as standard with ASP.NET Core applications, but no other applications, and the thing is, it is very simple to add to any dotnet core application.&lt;/p>
&lt;p>The first thing you will need to do is add the following nuget packages (at least) to your application:&lt;/p>
&lt;ul>
&lt;li>Microsoft.Extensions.Configuration&lt;/li>
&lt;li>Microsoft.Extensions.Configuration.FileExtensions&lt;/li>
&lt;li>Microsoft.Extensions.Configuration.Json&lt;/li>
&lt;/ul>
&lt;p>Once you have those packages installed, you can then add a new json file to the route of the project named &amp;ldquo;appsettings.json&amp;rdquo;. When you have added the new json file, right click on it in the solution explorer, click properties, and in the properties panel, change the &amp;ldquo;copy to output directory&amp;rdquo; option to &amp;ldquo;copy if newer&amp;rdquo;.&lt;/p>
&lt;p>Once you have the basics done, we can move on to the code:&lt;/p>
&lt;p>Your Program.cs file should now look something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-csharp" data-lang="csharp">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
&lt;span style="color:#66d9ef">var&lt;/span> config = &lt;span style="color:#66d9ef">new&lt;/span> ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(
&lt;span style="color:#e6db74">&amp;#34;appsettings.json&amp;#34;&lt;/span>,
optional: &lt;span style="color:#66d9ef">true&lt;/span>,
reloadOnChange: &lt;span style="color:#66d9ef">true&lt;/span>
)
.Build();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &amp;ldquo;optional: true&amp;rdquo; makes the appsettings.json file completely optional, if the file does not exist, the Build() will not throw an exception, and the &amp;ldquo;reloadOnChange: true&amp;rdquo; option will cause the config to be updated automatically when the json file is changed.&lt;/p>
&lt;p>It is as simple as that, as I said before, I don&amp;rsquo;t know why this is not part of the default template.&lt;/p></description></item><item><title>Microservices Introduction</title><link>https://www.darrenhorrocks.co.uk/microservices-introduction/</link><pubDate>Tue, 12 Nov 2019 20:42:47 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/microservices-introduction/</guid><description>&lt;p>Everybody has heard of the term &amp;ldquo;microservice&amp;rdquo;, but not many people know what they are, how they work, or how to implement one.
Many people think they are a complex system that needs a lot of complex setup and management. In fact, they are a very easy way
to remove heavy lifting from your web frontend to a more appropriate place.&lt;/p>
&lt;p>When developing a piece of web software with microservices doing some of the heavy processing, all you need is:&lt;/p>
&lt;ul>
&lt;li>Message Queue/Bus&lt;/li>
&lt;li>1 (or more) VM&amp;rsquo;s (to install and run your service)&lt;/li>
&lt;li>1 (or more) Web Frontend&lt;/li>
&lt;/ul>
&lt;p>The most basic way to explain what happens is:&lt;/p>
&lt;ol>
&lt;li>The web frontend serialises a model, and posts that model as a message to the queue&lt;/li>
&lt;li>A service installed on one or more VM&amp;rsquo;s listens to the queue&lt;/li>
&lt;li>A service will pick up the message from the queue, and:
&lt;ol>
&lt;li>Deserialise it, and process it. or,&lt;/li>
&lt;li>Deserialise it, not be able to process it, and place it back into the queue&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;p>And that is it.&lt;/p>
&lt;p>If there is more than one service, they will all be able to monitor the queue, but only one will process one message, but if there are 100s of messages, your 3 or 4 services, will process those in parallel (4 services processing a message each, rather than 1 service processing 1 at a time).&lt;/p>
&lt;p>&lt;img src="/images/microservice-figure-1.png" alt="queue based microservice architecture example" title="queue based microservice architecture example">&lt;/p>
&lt;p>As in the example above, this architecture can have different services listening to the same queue for different messages and performing different heavy duty tasks. Or simply decoupling certain critical tasks from the web frontend.&lt;/p>
&lt;p>In the next post, I will show you how to create this architecture using Windows Azure, C# and dotnet core.&lt;/p></description></item><item><title>Who should we blame?</title><link>https://www.darrenhorrocks.co.uk/who-should-we-blame/</link><pubDate>Wed, 18 Jan 2017 14:33:10 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/who-should-we-blame/</guid><description>&lt;p>Something that we all know is that there is a blame culture in business.
Something that not many people know is that we don’t need a blame culture in business.&lt;/p>
&lt;p>Take this scenario:&lt;/p>
&lt;ul>
&lt;li>Worker A is tasked with Job A to be performed weekly&lt;/li>
&lt;li>Worker B is tasked with Job B to be performed daily&lt;/li>
&lt;li>One day, Worker B is away, and Job B needs to be performed&lt;/li>
&lt;li>Worker A is asked to perform Job B by Manager C&lt;/li>
&lt;li>Worker A has no previous knowledge of Job B, but is competent, so performs Job B&lt;/li>
&lt;/ul>
&lt;p>Now in this scenario, we have a competent worker who has been asked to do something they are not used to doing, and because it is a daily process, it is time critical that it is done that day.&lt;/p>
&lt;p>An amount of days go by and worker B returns and continues performing Job B, only to find that a small, yet critical step has been missed by Worker A meaning that Job B has not been performed correctly.&lt;/p>
&lt;p>Who is to blame:&lt;/p>
&lt;ul>
&lt;li>Worker B for not properly documenting the processes of Job B?&lt;/li>
&lt;li>Worker A for not properly following the existing documentation of Job B?&lt;/li>
&lt;li>Worker A for not properly performing Job B?&lt;/li>
&lt;li>Manager C for practices that mean only Worker B is qualified to perform Job B?&lt;/li>
&lt;li>Manager C for asking Worker A who has no knowledge of Job B to perform Job B?&lt;/li>
&lt;/ul>
&lt;p>The answer? Nobody. None of these people should be blamed for any of these reasons because the culture of blame is not productive.&lt;/p>
&lt;p>What should happen after this issue is.&lt;/p>
&lt;p>Worker B confers with Worker A to find out exactly what problem this has caused, if any. Worker B should then create a solution and display its validity to Manager C. The solution should then be deployed. After this, Worker B should then update or create the documentation to Job B, and if possible, restructure Job B in a way where the issue is no longer possible. This will allow any future worker will be able to perform Job B without this issue arising again.&lt;/p>
&lt;p>What happened here is, the company did not waste time finding out &lt;strong>who&lt;/strong> was the root cause of the issue, they wisely spend money finding out &lt;strong>what&lt;/strong> was the root cause of the issue and ensured that it could no longer happen.&lt;/p>
&lt;p>Knowing who caused the problem does not fix the problem and it does not save you any money, as a business, &lt;strong>knowing who caused a problem, costs you money&lt;/strong>.&lt;/p></description></item><item><title>The Battle for Proper Working Hours</title><link>https://www.darrenhorrocks.co.uk/working-hours/</link><pubDate>Mon, 09 Jan 2017 22:42:10 +0000</pubDate><guid>https://www.darrenhorrocks.co.uk/working-hours/</guid><description>&lt;p>For many developers, the &lt;em>normal&lt;/em> 9 - 5 shift does not really apply.
Development is a creative job, and you cannot force more creativity from more hours.&lt;/p>
&lt;p>For many developers, the normal 9 - 5 shift does not really apply. Development is a creative job, and you cannot force more creativity from more hours.
Management seldom understand this and it can be difficult to articulate the point to a non-technically-minded or non-creative-minded manager.
Management are usually not open to offering flexible work time too, and if they are, it is usually within very rigid parameters: &amp;ldquo;Must be in 9:00 - 17:00, or 8:30 - 16:30&amp;rdquo;, which is not very flexible.&lt;/p>
&lt;p>Real flexibility comes in when you are given an amount of work to complete, and a deadline to complete it by. The amount of hours you work to achieve that deadline is irrelevant.
While this is the complete opposite end of the scale to rigidly set, long hours, it is how intelligent, salaried, well behaved employees should be treated by any goal oriented company (results rather than throughput).&lt;/p>
&lt;p>As for the hours, management cannot expect to force extra creativity from an employee that has used up their creativity for the day, for whatever reason it has been used up. If i was to come into the office and spend 2 solid hours coming up with the best work of my life, a manager would expect me to continue down that road for another 6 hours, but I, and many others know, it is not physically or mentally possible. It is exhausting while it is happening, but, that vein of gold is there for the taking, once the vein is used up, its gone.&lt;/p>
&lt;p>Therefore the concept of 40 hours, or 37.5 hours or even 35 hours does not work, especially in the tech industry in the modern era. I, and many other developers, can get a job specified as a 5 day job, done in 2 days if we were capable as humans to do this, but, the job takes 5, or 6 or 8 days, because it is not possible to ever work at 100% capacity, never mind for any amount of time.&lt;/p>
&lt;p>My employer recently reduced our working hours from 40 to 37.5 and set non-carry-over flexitime in place (with core hours of 10:00 - 15:00, for the convenience of scheduling meetings) (so, not flexitime, but, less rigid), but had to throw in the quip &amp;ldquo;but i will still be doing 60 hour weeks&amp;rdquo;, which seemed to be a jab at the &amp;ldquo;lazy employees&amp;rdquo; getting their way.
The problem is, he might be at work for 60 hours, but he sure is not working for 60 hours.&lt;/p>
&lt;p>Why is it such a battle to get employers and management to realise, if you want more from your employees, have them &lt;strong>at work less&lt;/strong>, and have them &lt;strong>working more&lt;/strong>.&lt;/p></description></item><item><title>The Pain of Developing a Xamarin App for OSX and iOS</title><link>https://www.darrenhorrocks.co.uk/osx-and-ios-pain-developing-a-xamarin-app/</link><pubDate>Mon, 09 Jan 2017 23:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/osx-and-ios-pain-developing-a-xamarin-app/</guid><description>&lt;p>When developing applications for Windows, even Windows 10 or some flavour of Linux or BSD, you pretty much have free reign over what you want to do. When it comes to Android, iOS and Mac OSX, it is a different story.&lt;/p>
&lt;p>Well not exactly with OSX, that is more of a recent addition (restriction) in Sierra.&lt;/p>
&lt;p>I have spent the last five weeks bootstrapping and building a client application using xamarin on Mac OSX. I chose to use El Capitan, as that was what I could get to run on the hackintosh.&lt;/p>
&lt;p>Everything was running well until today, when we had a new starter who came in with a brand-new MacBook running Sierra. We set up the build environment, we set up the extras that were required, built the solution and everything was fine.&lt;/p>
&lt;p>Until, we tried to run the application that had been built from the solution. First, there was an immediate crash when the application attempted to load &lt;code>System.Reflection.Emit.DynamicMethod&lt;/code>, which didn’t exist on the platform, even though it did. It turns out that even though the file exists, it is not compatible with the new security settings in Sierra. There is a way to disable the use of dynamic code generation, which is to tell Ninject (our DI library) to use reflection, rather than code generation.&lt;/p>
&lt;p>Once we got past this, we then got an almost immediate crash while attempting to load &lt;code>System.Reflection.Emit.ILGenerator&lt;/code>, which given that we are using reflection, not code generation, we should not be loading any code generation libraries at all. But the main Ninject library references them and requires them.&lt;/p>
&lt;p>In the midst of this, another team began prototyping an iOS app with xamarin, and ran into a very similar issue, though this was easily solved with setting ninject to use reflection.&lt;/p>
&lt;p>With the OSX app, the solution ended up being, &lt;em>switching from the Xamarin.Mac Mobile framework, to the Xamarin.Mac .NET 4.5 framework&lt;/em>, which then allowed for much more compatibility with ninject and some of our other libraries.&lt;/p>
&lt;p>It turns out, the route cause of all of this, is the operating system’s kernel. Neither like code generation, and iOS outright bans it. I couldn’t tell you why Seirra does not like code generation, but it does not.&lt;/p>
&lt;p>&lt;strong>Lesson learned:&lt;/strong> use the full framework with the largest feature set, rather than the minimal framework with the reduced feature set for negligible performance gains. Computers are fast, very fast, these days&amp;hellip; No need to skimp.&lt;/p></description></item><item><title>739s</title><link>https://www.darrenhorrocks.co.uk/739/</link><pubDate>Tue, 01 May 2012 20:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/739/</guid><description/></item><item><title>733s</title><link>https://www.darrenhorrocks.co.uk/733/</link><pubDate>Wed, 25 Apr 2012 20:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/733/</guid><description/></item><item><title>725s</title><link>https://www.darrenhorrocks.co.uk/725/</link><pubDate>Mon, 15 Aug 2011 23:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/725/</guid><description/></item><item><title>624s</title><link>https://www.darrenhorrocks.co.uk/624/</link><pubDate>Tue, 30 Nov 2010 23:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/624/</guid><description/></item><item><title>629s</title><link>https://www.darrenhorrocks.co.uk/629/</link><pubDate>Tue, 30 Nov 2010 23:00:00 +0100</pubDate><guid>https://www.darrenhorrocks.co.uk/629/</guid><description/></item></channel></rss>