<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jeffrey Rennie on Medium]]></title>
        <description><![CDATA[Stories by Jeffrey Rennie on Medium]]></description>
        <link>https://medium.com/@surferjeff?source=rss-8fbeff6da8a3------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*fLqlKsD_FrhdmqN6egeyrg.jpeg</url>
            <title>Stories by Jeffrey Rennie on Medium</title>
            <link>https://medium.com/@surferjeff?source=rss-8fbeff6da8a3------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 06 Jun 2026 07:19:12 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@surferjeff/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Dear C# Developers, Just #@!&ing Learn JavaScript Already]]></title>
            <link>https://itnext.io/dear-c-developers-just-ing-learn-javascript-already-9b5e26c50c4c?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/9b5e26c50c4c</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Mon, 29 Sep 2025 18:14:43 GMT</pubDate>
            <atom:updated>2025-09-29T23:13:09.547Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PFW7Oc0ORqWn9-MfVpm_cA.jpeg" /><figcaption>Website user is ready to slap you upside the head for not learning JavaScript</figcaption></figure><p>I get it. I too have dipped my toes into the cesspool of JavaScript and recoiled in horror.</p><p><a href="https://react.dev/">React</a> is tangled mess, and every new version of React brings a new <a href="https://react.dev/blog/2022/03/08/react-18-upgrade-guide#other-breaking-changes">tangled mess of breaking changes</a>. Yesterday I followed the <a href="https://dev.to/yugjadvani/mastering-server-side-rendering-ssr-in-react-19-with-vite-the-ultimate-guide-for-developers-4mgm">official instructions</a> to create a React app with TypeScript and got a <a href="https://gist.github.com/surferjeff/088f2001c528d05f43b0e344eb7fcbb3">compile error</a>. I asked Gemini for help, and it suggested a mess of changes to configuration files that I’ll never get right. It was hopeless and I gave up.</p><p><a href="https://www.npmjs.com/">NPM</a> and everything built with NPM is a house of cards built on top of a minefield. I work on some websites built with <a href="https://vuejs.org/">Vue</a>, and every time I upgrade one little dependency, I spend a day unbreaking everything that broke as a result of the upgrade. <a href="https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide">Dependabot</a> reports new vulnerabilities in my Vue projects’ dependencies at least once a week. Most of Dependabot’s patches fail to compile.</p><p>Running JavaScript on the server was a bad idea 15 years ago, and that hasn’t changed.</p><p>I won’t sweep these issues under the rug and attempt to persuade you to adopt a JavaScript framework.<strong> </strong>On the other hand, <strong>your refusal to learn JavaScript is causing you and your users more pain than just #@!&amp;ing learning JavaScript.</strong> Refusing to learn JavasScript<strong> </strong>makes about as much sense as building websites and refusing to learn HTML.</p><p>If every C# developer were half as skilled in JavaScript as they were in C#, then <a href="https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/start-mvc?view=aspnetcore-9.0&amp;tabs=visual-studio">ASP.NET Core MVC</a> would be sufficient to build any enterprise app. There would be no need for <a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">Blazor</a>, and the Web would be a better place if Blazor never existed.</p><h4>The trouble with Blazor</h4><p>Blazor is a combination of two seriously flawed and disparate technologies that, when combined, leave the programmer with more problems than solutions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8nrnDuMj2_qmZxTjV5ismw.jpeg" /><figcaption>A user suffering from a website built with Blazor</figcaption></figure><p>The first technology is <strong>Blazor Web Assembly</strong>. It compiles C# code into <a href="https://webassembly.org/">Web Assembly</a> and runs it in the browser. It’s a fine idea in theory, but in practice, it punishes a website’s users. On a mobile network, users spend over 3 seconds downloading about 10 MB of .wasm and .dll files to interact with the a Blazor web page. 10 MB of data transfer just cost a user in <a href="https://www.accrediteddebtrelief.com/blog/which-countries-get-the-best-value-mobile-data/">Malawi roughly 1% of their monthly salary</a>. And decompressing the files and then compiling them from web assembly into machine code drained their phone’s battery. Also, Blazor Web Assembly apps are laggy and memory hungry. Of the dozens of JavaScript frameworks available today, you literally can’t choose one <a href="https://krausest.github.io/js-framework-benchmark/current.html">slower or more memory hungry than Blazor</a>. The average device browsing the internet today is a <a href="https://www.bankmycell.com/blog/average-lifespan-of-smartphone">2-year</a> old <a href="https://gs.statcounter.com/os-market-share">Android </a>phone that <a href="https://globaldigitalinclusion.org/2024/09/10/the-price-of-the-world-in-your-pocket-a-price-only-some-can-afford/">cost less than $200</a>. Blazor apps struggle on such devices.</p><p>Blazor Web assembly will not only punish your users, it will also punish you when it’s time to debug a misbehaving website. I’m not talking about running the application in your debugging environment. I’m talking about when a tester hands you their laptop and shows you a bug in the production website that only happens on that laptop, so you open the browser’s debugging tools and see… absolutely nothing useful. Good luck!</p><p>The second technology is <strong>Blazor Server</strong>. It ships a small JavaScript library to the browser which opens a <a href="https://en.wikipedia.org/wiki/WebSocket">WebSocket</a> back to the server. At least it doesn’t drain users’ batteries or bank accounts, but users will still suffer when there is a hiccup in that WebSocket connection to the server. Maybe the server crashed or restarted, or the user’s phone switched cell towers, or was routed to a different server in the server farm. A user was filling out page 9 of a 10-page form, and now they have to start over on page one. They’re not happy.</p><p>And like Blazor Web Assembly, Blazor Server will punish you when it comes time to debug. If you’re lucky enough to see messages passed on the WebSocket (I didn’t), you’ll have to decode the binary <a href="https://en.wikipedia.org/wiki/WebSocket">BlazorPack</a> protocol. Good luck!</p><p>Blazor Server punishes another innocent party spared by Blazor Web Assembly: <a href="https://en.wikipedia.org/wiki/Site_reliability_engineering">Site Reliability Engineers</a>. They can’t simply shut down an underutilized Blazor server because the few users still connected to that server would see the website completely freeze. This makes regular maintenance like rolling out new versions of your website, scaling up and down to meet load, and moving web servers from one machine to another much more complicated.</p><h3><strong>If Blazor is a train wreck and JavaScript frameworks are hot garbage, then what’s a C# programmer to do?</strong></h3><p>Learn some JavaScript. Just the basics will take you a long way.</p><p>There’s good news. JavaScript in the browser in 2025 is not the crumbling <a href="https://en.wikipedia.org/wiki/Tower_of_Babel">Tower of Babel</a> it was in 2009. Internet Explorer is long gone. Microsoft Edge, Google Chrome, Firefox and Safari all conform to web standards. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules">Modules</a> prevent global namespace pollution and other headaches.</p><p>To demonstrate how powerful and easy it is to add a little JavaScript, let’s consider the default demo app you get when you run dotnet new blazor.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/491/1*d8L36_y0dj53OAgIeyv0gw.png" /></figure><p><a href="https://github.com/surferjeff/blazor-compared/tree/main/MVCApp">I rebuilt this application as an MVC app</a> and reproduced all Blazor’s interactive features with only <em>6 lines of JavaScript</em>. Let’s explore the app.</p><p>First, Blazor’s components are nothing special. I used <a href="https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-9.0">MVC’s partial views</a> instead of Blazor’s components. Let’s compare.</p><p><a href="https://github.com/surferjeff/blazor-compared/blob/main/BlazorApp/Shared/SurveyPrompt.razor">Blazor</a>:</p><pre>&lt;div class=&quot;alert alert-secondary mt-4&quot;&gt;<br>    &lt;span class=&quot;oi oi-pencil me-2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;<br>    &lt;strong&gt;@Title&lt;/strong&gt;<br><br>    &lt;span class=&quot;text-nowrap&quot;&gt;<br>        Please take our<br>        &lt;a target=&quot;_blank&quot; class=&quot;font-weight-bold link-dark&quot; href=&quot;https://go.microsoft.com/fwlink/?linkid=2149017&quot;&gt;brief survey&lt;/a&gt;<br>    &lt;/span&gt;<br>    and tell us what you think.<br>&lt;/div&gt;<br><br>@code {<br>    // Demonstrates how a parent component can supply parameters<br>    [Parameter]<br>    public string? Title { get; set; }<br>}</pre><p><a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCApp/Views/Shared/_SurveyPrompt.cshtml">MVC</a>:</p><pre>@model string<br><br>&lt;div class=&quot;alert alert-secondary mt-4&quot;&gt;<br>    &lt;span class=&quot;oi oi-pencil me-2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;<br>    &lt;strong&gt;@Model&lt;/strong&gt;<br><br>    &lt;span class=&quot;text-nowrap&quot;&gt;<br>        Please take our<br>        &lt;a target=&quot;_blank&quot; class=&quot;font-weight-bold link-dark&quot; href=&quot;https://go.microsoft.com/fwlink/?linkid=2149017&quot;&gt;brief survey&lt;/a&gt;<br>    &lt;/span&gt;<br>    and tell us what you think.<br>&lt;/div&gt;</pre><p>The differences between these two source files are so small they’re not even worth discussing. I see no reason to prefer the Blazor component over the MVC partial view.</p><p>Now let’s examine the 3-bars button in the top right corner, that when clicked, displays the menu.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/487/1*RSZKQWHc0VoaCUy8WWF-SA.png" /></figure><p>Blazor implemented the menu with the <a href="https://github.com/surferjeff/blazor-compared/blob/main/BlazorApp/Shared/NavMenu.razor">following code</a>. Unbelievably, it performs a network round trip to the server just to open the menu. If the network request is delayed by 2 seconds, then the user will wait 2 seconds to see the menu drop down. More likely, they’ll give up and try your competitor’s website.</p><pre>&lt;div class=&quot;top-row ps-3 navbar navbar-dark&quot;&gt;<br>    &lt;div class=&quot;container-fluid&quot;&gt;<br>        &lt;a class=&quot;navbar-brand&quot; href=&quot;&quot;&gt;BlazorApp&lt;/a&gt;<br>        &lt;button title=&quot;Navigation menu&quot; class=&quot;navbar-toggler&quot; @onclick=&quot;ToggleNavMenu&quot;&gt;<br>            &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;<br>        &lt;/button&gt;<br>    &lt;/div&gt;<br>&lt;/div&gt;<br><br>&lt;div class=&quot;@NavMenuCssClass&quot; @onclick=&quot;ToggleNavMenu&quot;&gt;<br>    &lt;nav class=&quot;flex-column&quot;&gt;<br>        &lt;div class=&quot;nav-item px-3&quot;&gt;<br>            &lt;NavLink class=&quot;nav-link&quot; href=&quot;&quot; Match=&quot;NavLinkMatch.All&quot;&gt;<br>                &lt;span class=&quot;oi oi-home&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Home<br>            &lt;/NavLink&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;nav-item px-3&quot;&gt;<br>            &lt;NavLink class=&quot;nav-link&quot; href=&quot;counter&quot;&gt;<br>                &lt;span class=&quot;oi oi-plus&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Counter<br>            &lt;/NavLink&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;nav-item px-3&quot;&gt;<br>            &lt;NavLink class=&quot;nav-link&quot; href=&quot;fetchdata&quot;&gt;<br>                &lt;span class=&quot;oi oi-list-rich&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Fetch data<br>            &lt;/NavLink&gt;<br>        &lt;/div&gt;<br>    &lt;/nav&gt;<br>&lt;/div&gt;<br><br>@code {<br>    private bool collapseNavMenu = true;<br><br>    private string? NavMenuCssClass =&gt; collapseNavMenu ? &quot;collapse&quot; : null;<br><br>    private void ToggleNavMenu()<br>    {<br>        collapseNavMenu = !collapseNavMenu;<br>    }<br>}</pre><p>Here’s the <a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCApp/Views/Shared/_NavMenu.cshtml">MVC version</a>. It actually doesn’t need any JavaScript to implement the drop down when clicked. Even when the network is unavailable, the menu drops down immediately.</p><pre>&lt;div class=&quot;top-row ps-3 navbar navbar-dark&quot;&gt;<br>    &lt;div class=&quot;container-fluid&quot;&gt;<br>        &lt;a class=&quot;navbar-brand&quot; href=&quot;&quot;&gt;BlazorApp&lt;/a&gt;<br>        &lt;label for=&quot;toggle-menu&quot;&gt;<br>            &lt;div title=&quot;Navigation menu&quot; class=&quot;navbar-toggler&quot;&gt;<br>                &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;<br>            &lt;/div&gt;<br>        &lt;/label&gt;<br>    &lt;/div&gt;<br>&lt;/div&gt;<br><br>&lt;input type=&quot;checkbox&quot; id=&quot;toggle-menu&quot; class=&quot;visually-hidden&quot;&gt;<br><br>&lt;div id=&quot;nav-menu&quot;&gt;<br>    &lt;nav class=&quot;flex-column&quot; hx-boost=true hx-target=&quot;#main-article&quot;&gt;<br>        &lt;div class=&quot;nav-item px-3&quot;&gt;<br>            &lt;a class=&#39;nav-link @classForPath(&quot;/&quot;)&#39; href=&quot;/&quot;&gt;<br>                &lt;span class=&quot;oi oi-home&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Home<br>            &lt;/a&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;nav-item px-3&quot;&gt;<br>            &lt;a class=&#39;nav-link @classForPath(&quot;/counter&quot;)&#39; href=&quot;/counter&quot;&gt;<br>                &lt;span class=&quot;oi oi-plus&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Counter<br>            &lt;/a&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;nav-item px-3&quot;&gt;<br>            &lt;a class=&#39;nav-link @classForPath(&quot;/fetchdata&quot;)&#39; href=&quot;/fetchdata&quot;&gt;<br>                &lt;span class=&quot;oi oi-list-rich&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt; Fetch data<br>            &lt;/a&gt;<br>        &lt;/div&gt;<br>    &lt;/nav&gt;<br>&lt;/div&gt;<br><br>@functions {<br>    string classForPath(string path) {<br>        return path == Context.Request.Path ? &quot;active&quot; : &quot;&quot;;<br>    }<br>}<br><br></pre><p>It employs a handy feature of HTML and CSS: <a href="https://css-tricks.com/the-checkbox-hack/">the state of a checkbox can control the appearance of another element</a>. Here’s the relevant CSS:</p><pre>#toggle-menu:checked ~ #nav-menu {<br>    display: block;<br>}</pre><p>Next, let’s re-implement the Counter with a little JavaScript:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/321/1*zzdiB0xEwcJfPh1HRniNfQ.png" /></figure><p>First, <a href="https://github.com/surferjeff/blazor-compared/blob/main/BlazorApp/Pages/Counter.razor">Blazor’s implementation</a>:</p><pre>@page &quot;/counter&quot;<br><br>&lt;PageTitle&gt;Counter&lt;/PageTitle&gt;<br><br>&lt;h1&gt;Counter&lt;/h1&gt;<br><br>&lt;p role=&quot;status&quot;&gt;Current count: @currentCount&lt;/p&gt;<br><br>&lt;button class=&quot;btn btn-primary&quot; @onclick=&quot;IncrementCount&quot;&gt;Click me&lt;/button&gt;<br><br>@code {<br>    private int currentCount = 0;<br><br>    private void IncrementCount()<br>    {<br>        currentCount++;<br>    }<br>}</pre><p>And now, the <a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCApp/Views/Home/Counter.cshtml">MVC version</a>:</p><pre>@{<br>    ViewData[&quot;Title&quot;] = &quot;Counter&quot;;<br>}<br><br>&lt;h1&gt;Counter&lt;/h1&gt;<br><br>&lt;p role=&quot;status&quot;&gt;Current count: &lt;span id=&quot;countSpan&quot;&gt;0&lt;/span&gt;&lt;/p&gt;<br><br>&lt;button id=&quot;countBtn&quot; class=&quot;btn btn-primary&quot;&gt;Click me&lt;/button&gt;<br><br>&lt;script type=&quot;module&quot;&gt;<br>    let count = 0;<br>    document.getElementById(&quot;countBtn&quot;).onclick =<br>        () =&gt; document.getElementById(&quot;countSpan&quot;).innerText = ++count;<br>&lt;/script&gt;</pre><p>See type=”module” on the script tag? That confines the scope of count to the namespace bounded by &lt;script&gt; and &lt;/script&gt;. count never enters the global namespace. This avoids name collisions that caused many headaches before type=”module” became a thing.</p><p>Also, scripts with type=”module” are always <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script#defer">deferred</a>. They don’t get evaluated until the document is completely parsed. This means there’s no need to put them in a special @section Scripts {} block.</p><p>If you’re a C# programmer and you see something confusing in the JavaScript code, please leave a comment.</p><p>Again, the MVC version has the advantage that the count will increment immediately with each click, no matter what condition the network is in.</p><p>Finally, let’s examine the Weather Forecasts page:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/316/1*Y3VvMwr5zZnKsPB4DG7pMQ.png" /></figure><p>Here’s <a href="https://github.com/surferjeff/blazor-compared/blob/main/BlazorApp/Pages/FetchData.razor">Blazor’s implementation</a>.</p><pre>@page &quot;/fetchdata&quot;<br><br>&lt;PageTitle&gt;Weather forecast&lt;/PageTitle&gt;<br><br>@using BlazorApp.Data<br>@inject WeatherForecastService ForecastService<br><br>&lt;h1&gt;Weather forecast&lt;/h1&gt;<br><br>&lt;p&gt;This component demonstrates fetching data from a service.&lt;/p&gt;<br><br>@if (forecasts == null)<br>{<br>    &lt;p&gt;&lt;em&gt;Loading...&lt;/em&gt;&lt;/p&gt;<br>}<br>else<br>{<br>    &lt;table class=&quot;table&quot;&gt;<br>        &lt;thead&gt;<br>            &lt;tr&gt;<br>                &lt;th&gt;Date&lt;/th&gt;<br>                &lt;th&gt;Temp. (C)&lt;/th&gt;<br>                &lt;th&gt;Temp. (F)&lt;/th&gt;<br>                &lt;th&gt;Summary&lt;/th&gt;<br>            &lt;/tr&gt;<br>        &lt;/thead&gt;<br>        &lt;tbody&gt;<br>            @foreach (var forecast in forecasts)<br>            {<br>                &lt;tr&gt;<br>                    &lt;td&gt;@forecast.Date.ToShortDateString()&lt;/td&gt;<br>                    &lt;td&gt;@forecast.TemperatureC&lt;/td&gt;<br>                    &lt;td&gt;@forecast.TemperatureF&lt;/td&gt;<br>                    &lt;td&gt;@forecast.Summary&lt;/td&gt;<br>                &lt;/tr&gt;<br>            }<br>        &lt;/tbody&gt;<br>    &lt;/table&gt;<br>}<br><br>@code {<br>    private WeatherForecast[]? forecasts;<br><br>    protected override async Task OnInitializedAsync()<br>    {<br>        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);<br>        StateHasChanged();<br>    }<br>}</pre><p>MVC’s implementation is split across two handlers, /FetchData and /Forecasts. First, the <a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCApp/Views/Home/FetchData.cshtml">view for /FetchData</a>:</p><pre>@{<br>    ViewData[&quot;Title&quot;] = &quot;Weather forecast&quot;;<br>}<br><br>&lt;h1&gt;Weather forecast&lt;/h1&gt;<br><br>&lt;p&gt;This component demonstrates fetching data from a service.&lt;/p&gt;<br><br>&lt;div id=&quot;tableHere&quot;&gt;<br>    &lt;em&gt;Loading...&lt;/em&gt;<br>&lt;/div&gt;<br><br>&lt;script type=&quot;module&quot;&gt;<br>    const response = await fetch(&quot;/Forecasts&quot;);<br>    if (!response.ok) throw new Error(`Bad response: ${response.status}`);<br>    document.getElementById(&quot;tableHere&quot;).innerHTML = await response.text();<br>&lt;/script&gt;</pre><p>The JavaScript code fetches /Forecasts from the server, and replaces the <em>Loading…</em> place holder. This will work even if the /Forecasts request arrives at a different server than the preceding /FetchData request. In fact, it will usually work even when the two servers are running different versions of the website. <a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCApp/Views/Home/Forecasts.cshtml">/Forecasts’ view</a> requires no JavaScript:</p><pre>@model MVCApp.Data.WeatherForecast[];<br><br>@{<br>    Layout = &quot;_NoLayout&quot;;<br>}<br><br>&lt;table class=&quot;table&quot;&gt;<br>    &lt;thead&gt;<br>        &lt;tr&gt;<br>            &lt;th&gt;Date&lt;/th&gt;<br>            &lt;th&gt;Temp. (C)&lt;/th&gt;<br>            &lt;th&gt;Temp. (F)&lt;/th&gt;<br>            &lt;th&gt;Summary&lt;/th&gt;<br>        &lt;/tr&gt;<br>    &lt;/thead&gt;<br>    &lt;tbody&gt;<br>        @foreach (var forecast in @Model)<br>        {<br>            &lt;tr&gt;<br>                &lt;td&gt;@forecast.Date.ToShortDateString()&lt;/td&gt;<br>                &lt;td&gt;@forecast.TemperatureC&lt;/td&gt;<br>                &lt;td&gt;@forecast.TemperatureF&lt;/td&gt;<br>                &lt;td&gt;@forecast.Summary&lt;/td&gt;<br>            &lt;/tr&gt;<br>        }<br>    &lt;/tbody&gt;<br>&lt;/table&gt;</pre><p>And we’re done. That’s it. We’ve reproduced every interactive feature of the Blazor default application with only 6 lines of JavaScript.</p><p>The benefits are many. I added less than 1k of JavaScript to the website instead of 10MB of wasm. The UI responds immediately instead of waiting for a round trip from the server. Debugging with only the browser is straight-forward. I see plain HTML in the network tab, and I can step through each line of JavaScript I wrote. I can deploy a new version of my app without waiting for all users to disconnect from running instances.</p><p>Some readers may have heard alarm bells when they saw element.innerHtml = await response.text();in the code above. There’s no reason for alarm because the response came from a .cshtml file (a View) which did proper HTML escaping. Fetching a View and assigning the response to .innerHtml is no more dangerous than simply providing a link to the same URL.</p><p>Indeed, assigning .innerHtml to View responses is a simple way to transform yourself from a backend engineer to a full-stack engineer. No hydration or dehydration is necessary. The network tab always displays readable HTML. If you like this technique, then you’ll love <a href="https://htmx.org/">HTMX</a>. HTMX makes it easy to dynamically update a page with View responses without writing any JavaScript, but more about that later.</p><h4>A Hello World application and a code base of 100,000 lines of JavaScript are two very different beasts.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*URWyNjB4Qduk32KQWX0c5A.jpeg" /><figcaption>Two very different beasts</figcaption></figure><p>Reading 10 lines of JavaScript is rarely difficult. Managing a project with 100,000 lines of JavaScript is never easy.</p><p>JavaScript modules, as I demonstrated above, are the first step towards corralling complexity. In short, they work a lot like .cs files and using statements. They don’t pollute the global namespace. Importing a JavaScript module multiple times results in it getting evaluated only once. One module can selectively import the symbols it needs from another module. A module can rename symbols as it imports them.</p><h4>TypeScript tames the JavaScript beast.</h4><p>Microsoft (actually, the <a href="https://en.wikipedia.org/wiki/Anders_Hejlsberg">same person </a>who designed C#) invented TypeScript, so I’m surprised they don’t spend more energy promoting it to C# developers. They publish the Nuget package <a href="https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild/">Microsoft.TypeScript.MSBuild</a> which compiles TypeScript files in a .csproj automatically. Microsoft also publishes <a href="https://learn.microsoft.com/en-us/visualstudio/javascript/tutorial-aspnet-with-typescript?view=vs-2022">instructions on how to add TypeScript to you MVC project</a>. The instructions make it easy to add TypeScript to your project without running an npm command. Thank you Microsoft!</p><p>The benefits of using a type-safe language are well known to C# developers. Building an application with TypeScript instead of JavaScript means that far more mistakes will be found at compile time rather than runtime. The benefits are really valuable when working with a large team and a large code base.</p><p>Let’s peek at what the /counter implementation looks like with TypeScript. First, <a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCAppWithTypeScript/Views/Home/Counter.cshtml">Counter.cshtml</a>:</p><pre>@{<br>    ViewData[&quot;Title&quot;] = &quot;Counter&quot;;<br>}<br><br>&lt;h1&gt;Counter&lt;/h1&gt;<br><br>&lt;p role=&quot;status&quot;&gt;Current count: &lt;span id=&quot;countSpan&quot;&gt;0&lt;/span&gt;&lt;/p&gt;<br><br>&lt;button id=&quot;countBtn&quot; class=&quot;btn btn-primary&quot;&gt;Click me&lt;/button&gt;<br><br>&lt;script type=&quot;module&quot; src=&quot;~/ts/Counter.js&quot;&gt;&lt;/script&gt;</pre><p>And <a href="https://github.com/surferjeff/blazor-compared/blob/main/MVCAppWithTypeScript/scripts/Counter.ts">Counter.ts</a>:</p><pre>let count = 0;<br>document.getElementById(&quot;countBtn&quot;).onclick =<br>    () =&gt; document.getElementById(&quot;countSpan&quot;).innerText = String(++count);</pre><p>And here’s my favorite part. Below is Counter.js, the code compiled from Counter.ts above and served to the browser. This is the code I step through when a tester hands me their laptop with a misbehaving website:</p><pre>let count = 0;<br>document.getElementById(&quot;countBtn&quot;).onclick =<br>    () =&gt; document.getElementById(&quot;countSpan&quot;).innerText = String(++count);<br>//# sourceMappingURL=Counter.js.map</pre><p>Aside from comments and white space, it looks identical to the original TypeScript. There’s no magic. When it comes time to debug an app in production, I don’t even need a source map.</p><p>One thing missing from Microsoft’s TypeScript tools for ASP.NET core is hot reloading. I find writing code without hot reloading to be frustrating, so I built <a href="https://github.com/surferjeff/blazor-compared/tree/main/MVCAppWithTypeScript">MVCAppWithTypeScript</a> to perform hot reloading with <a href="https://esbuild.github.io/">esbuild</a>. I recommend using it as a template if you’d like to add TypeScript to your ASP.NET core MVC project.</p><h4>JavaScript isn’t Hard.</h4><p>I learned C#, JavaScript, Rust, and F# in that order. It took me about 3 times as long to learn each of Rust and F# than it took to learn JavaScript. It’s not hard. Yes, there are <a href="https://medium.com/@poojaauma/7-mind-blowing-javascript-quirks-that-will-surprise-you-02340b6d3c01">lots of quirks</a> in JavaScript, but TypeScript disambiguates some of those quirks, and I’d rather live with the quirks than make my users and myself suffer Blazor.</p><h4>In conclusion</h4><p>Before building a project with Blazor, I recommend just @#!&amp;ing learning JavaScript. It’s doable. It’s not the nightmare it used to be. TypeScript will convert some run-time mysteries of JavaScript into highly readable compile-time errors.</p><p>Also, feel free to ignore the Next Great JavaScript framework that comes out next week. It’s trap. It will work great on day 1, but 2 months and 3 versions later, you’ll regret ever hearing about it.</p><p>HTMX is another way to dynamically update a page without the flaws of Blazor and without writing a line of JavaScript. I published a <a href="https://www.udemy.com/course/enchancing-an-aspnet-mvc-app-with-htmx/?referralCode=0041903ABD623292DC68">Udemy course</a> on how to add HTMX to your ASP.NET core MVC Application. Use the code NEVER-BLAZOR before October 25, 2025 to receive a 20% discount.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9b5e26c50c4c" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/dear-c-developers-just-ing-learn-javascript-already-9b5e26c50c4c">Dear C# Developers, Just #@!&amp;ing Learn JavaScript Already</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I built the same software 3 times, then Rust showed me a better way]]></title>
            <link>https://itnext.io/i-built-the-same-software-3-times-then-rust-showed-me-a-better-way-1a74eeb9dc65?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/1a74eeb9dc65</guid>
            <category><![CDATA[rust-programming-language]]></category>
            <category><![CDATA[pragramming]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Tue, 22 Jul 2025 16:30:49 GMT</pubDate>
            <atom:updated>2025-07-22T22:00:58.913Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MSHHhQabsEd2DczLoHWJLw.jpeg" /></figure><p>I build <a href="https://pdfsnake.com/aboutus.html">software</a> that manipulates PDF documents. Over 2 decades, I have built such software many different ways:</p><ol><li>With Python using <a href="https://ply.readthedocs.io/en/latest/index.html">PLY</a>.</li><li>With C++ using <a href="https://ftp.gnu.org/old-gnu/Manuals/flex-2.5.4/html_mono/flex.html">Flex</a> and <a href="https://www.gnu.org/software/bison/">Bison</a>.</li><li>With C++ building a <a href="https://en.wikipedia.org/wiki/Recursive_descent_parser">recursive-descent</a> parser.</li><li>With Rust building a <a href="https://en.wikipedia.org/wiki/Recursive_descent_parser">recursive-descent</a> parser.</li></ol><h4>The Rust implementation was 3 times faster than the C++ implementations because Rust showed me a new way to represent PDFs in memory.</h4><p>When building the fourth application in Rust, I tried to represent the PDF document in memory the same way I did in C++, but Rust wouldn’t let me. No matter what I tried, Rust reported errors with lifetimes and mutability. I spent about a week trying to convince the Rust compiler to let my code compile, but it wouldn’t yield. Finally, I gave up and redesigned the in-memory representation of PDF documents to conform to Rust’s rules. The result was a massive boost in performance and reduced memory consumption.</p><p>To be clear, I don’t think any of the performance differences between the C++ and Rust applications were due to the languages themselves. Had I represented the internal PDF structure in C++ the same way I had in Rust, I’d bet a dollar that the C++ code would have been faster than the Rust code. I had just never thought to write the C++ code that way.</p><h3>PDF Document Internals</h3><p>The internal structure of a PDF Document is complex. The <a href="https://pdfa.org/resource/iso-32000-1/">specification</a> for PDF version 1.7 is 756 pages long, and that doesn’t even describe lots of supporting technology needed to fully decode a PDF document like a full JPEG decoder, multiple font file decoders, compression algorithms and more.</p><p>However, the core skeleton of a PDF Document is actually quite simple, and not much different from JSON. Consider the following JSON representation of a family tree:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/481b615144dac8d372e07a1bbd0b8c4c/href">https://medium.com/media/481b615144dac8d372e07a1bbd0b8c4c/href</a></iframe><p>I’ll transform this JSON into something a little more like a PDF document, and then explain the transformations:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0a6cfca122cabc9f68a8081d33f6cdc1/href">https://medium.com/media/0a6cfca122cabc9f68a8081d33f6cdc1/href</a></iframe><p>The transformations explained:</p><ul><li>id fields have been removed, and replaced with a unique object number. Id was defined by the implicit JSON schema, but the object number is defined by the PDF specification.</li><li>Links to other objects are represented by a new symbol: R-<em>object-number</em>.</li><li>A look up table at the end of the file lists the first line number for each object. This makes it quick and easy to follow a link from one object to another.</li></ul><p>How would you represent this file format in memory, knowing that most PDF documents are too large to fit into memory, and a document and its objects will only be accessed from a single thread?</p><p>In C++, I represented the document like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b7a1009b61247c075e6f09d341f798f8/href">https://medium.com/media/b7a1009b61247c075e6f09d341f798f8/href</a></iframe><p>This code followed <a href="https://en.wikipedia.org/wiki/Object-oriented_programming">object oriented design</a> principles. Every object type, such as string, array, link, etc. was implemented with its own class derived from Object. Each class knew how to write itself to a PDF file. Every object was allocated on the heap. <a href="https://en.wikipedia.org/wiki/Reference_counting">Reference counts</a> safely kept each object in memory as long as it was needed. Circular references weren’t actually a problem for reasons that are outside the scope of this article.</p><p>Let’s tally the operations needed to destroy the following object:</p><pre>  {<br>    &quot;name&quot;: &quot;Michael Doe&quot;,<br>    &quot;birthday&quot;: [1995, 3, 10],<br>    &quot;father&quot;: R-0,<br>    &quot;mother&quot;: R-1<br>  }</pre><ul><li>free 8 Objects: 3 numbers + 2 links + 1 array + 1 string + 1 dictionary</li><li>decrement the doc’s reference count 8 times</li><li>decrement objects’ reference counts 7 times</li><li>free 5 strings</li><li>free 1 vector</li><li>free 1 hash_map</li></ul><p>That’s a total of 15 frees and 13 reference counts decremented. Objects may be scattered across the heap so <a href="https://en.wikipedia.org/wiki/Locality_of_reference">cache locality</a> is likely low, negatively impacting performance. In this context, there’s a code branch immediately after decrement. Code branches also negatively impact performance.</p><h4>This C++ code could not be directly translated to Rust because the Rust does not allow willy-nilly pointers like this.</h4><p>Look at the C++ code and try to answer a simple question: which lives longer, a document or an object in the document’s cache? It’s impossible to know just by looking at the code I provided. Rust requires an answer to this question or it will refuse to compile the code.</p><p>One way to maybe satisfy Rust’s compiler would be to use Rc&lt;&gt;, which is a smart pointer type that counts references. I was new to Rust when I was writing this code. I didn’t know or look for a reference-counting smart pointer type, because I thought a reference-counting smart pointer would throw away half the benefits of Rust’s borrow checker. I’d be sacrificing compile-time guarantees for runtime enforcement; it would have been no better than assert statements in C++. At that point, why even write the code in Rust? It would have been easier to write the code in C++.</p><p>Also, it would have been possible to implement something like the Object base class in Rust by using <a href="https://doc.rust-lang.org/book/ch10-02-traits.html">traits</a>, but I chose to use an <a href="https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html">enum</a> instead. Enums in Rust are a lot like C++’s <a href="https://en.cppreference.com/w/cpp/utility/variant.html">std::variant&lt;&gt;</a>, but Rust provides syntax to make working with enums easy and less error prone. Anyways, Rust’s enums were shiny and new to me and I wanted to try them. Let’s look at the Rust implementation:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cd8f963eab7a06a86d6559bc84424934/href">https://medium.com/media/cd8f963eab7a06a86d6559bc84424934/href</a></iframe><p>Rust’s lookup() function returns a copy of the object from the cache, whereas C++’s lookup() function returns a pointer to the object in the cache. I thought copying objects would substantially and negatively impact performance, but benchmarks and profilers showed me the performance benefits of using an enum far outweighed the cost of copying objects. Because objects are no longer modified in place, I added a set() function to update the document with changes to objects.</p><p>Compared to the C++ code, the Rust code allocates far fewer blocks on the heap.</p><p>Let’s tally the operations needed to destroy the same object:</p><pre>{<br>    &quot;name&quot;: &quot;Michael Doe&quot;,<br>    &quot;birthday&quot;: [1995, 3, 10],<br>    &quot;father&quot;: R-0,<br>    &quot;mother&quot;: R-1<br>  }</pre><ul><li>free 1 Vec&lt;&gt;</li><li>free 1 HashMap&lt;&gt;</li><li>free 5 strings</li></ul><p>For a total of 7 frees and 0 reference count decrements. Compare that to C++’s 15 frees and 13 reference count decrements.</p><p>These changes alone made my Rust program perform more than twice as fast as the same program implemented in C++.</p><p>I was so inspired by the performance benefits of reducing heap allocations that I switched the string implementation in the Rust code to a type that stores short strings in-place without allocating any memory. Only longer strings require heap allocations. So in the example above, keys like “name” and “birthday” require no heap allocations. Only longer strings like “Michael Doe” require a heap allocation. That reduces the tally for destroying the object above to:</p><ul><li>free 1 Vec&lt;&gt;</li><li>free 1 HashMap&lt;&gt;</li><li>free 1 string</li></ul><p>So 3 frees and 0 reference count decrements. Compare that to C++’s 15 frees and 13 reference count decrements.</p><p>The drastic reduction in allocations and reference counts and better cache locality made my Rust implementation about 3 times faster than my C++ implementation.</p><h3>Rust is not faster than C++</h3><p>It’s possible and not too difficult to optimize the C++ so it performs like my Rust code. In fact, I’d bet that with all the same optimizations applied, the C++ code would be faster. That’s not the point.</p><p>The point of this article is: <strong>the obvious, convenient way to write the code in Rust also turned out to be optimal for performance</strong>. That’s amazing.</p><p>I wrote the C++ code around 2005, when std::variant&lt;&gt; didn’t yet exist, std::string didn’t do <a href="https://cppdepend.com/blog/understanding-small-string-optimization-sso-in-stdstring/">small string optimization</a>, and OOP was the accepted wisdom. I haven’t written C++ intensively in about 10 years. Has the accepted wisdom of C++ programmers changed since then?</p><p>Also, is something like Rust’s enums available in your favorite programming language? Do you think your favorite programming language could match Rust’s performance on this task?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1a74eeb9dc65" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/i-built-the-same-software-3-times-then-rust-showed-me-a-better-way-1a74eeb9dc65">I built the same software 3 times, then Rust showed me a better way</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[git rebase or git merge? Neither.]]></title>
            <link>https://surferjeff.medium.com/git-rebase-or-git-merge-neither-8a98250040f3?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/8a98250040f3</guid>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Thu, 19 Jun 2025 00:48:16 GMT</pubDate>
            <atom:updated>2025-06-19T00:48:16.294Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cQu2OZOGqNq64N84ZsRF8g.jpeg" /></figure><p>I just watched some people argue about this, and I didn’t hear anyone describe my favorite solution, so I’ll describe it here. It leaves a perfect trail of breadcrumbs so if I make mistake, I can always rollback. Hopefully, it saves you some time and pain.</p><p>In this scenario, I’ve been working in my feature branch for two weeks. Of course, lots of changes have been committed to the main branch while I’ve been working in my feature branch. Now it’s time to pull changes from main.</p><pre>&gt; git status<br>On branch feature<br>nothing to commit, working tree clean<br>&gt; git checkout main<br>&gt; git pull<br>&gt; git checkout -b feature2<br>&gt; git merge --squash feature</pre><p>And then I resume my work in the feature2 branch.</p><h4>Why this is better than rebase or merge.</h4><ul><li>The git history is perfectly linear, thanks to--squash.</li><li>Changes are never overwritten or lost.</li><li>Resolving conflicts the wrong way can be undone later.</li></ul><h4>And yes, this is a serious flaw in git.</h4><p>I hate the fact that git provides at least 3 ways to do the most common task of any source control system: pull down recent changes from the main branch. The git command line is hostile to humans. I haven’t found anything better, though.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8a98250040f3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Functional Programming Languages Will Never Be Mainstream]]></title>
            <link>https://itnext.io/why-functional-programming-languages-will-never-be-mainstream-e1b5a8c9e4d8?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/e1b5a8c9e4d8</guid>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[programming-languages]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Thu, 02 Jan 2025 16:38:02 GMT</pubDate>
            <atom:updated>2025-02-06T08:16:48.991Z</atom:updated>
            <content:encoded><![CDATA[<p>Functional programming has been <a href="https://spectrum.ieee.org/functional-programming">the next big thing</a> for the past 65 years, so why do all functional programming languages together not exceed 5% of mind share according to <a href="https://spectrum.ieee.org/top-programming-languages-2024">IEEE</a>, <a href="https://www.tiobe.com/tiobe-index/">Tiobe</a>, etc? This article describes two reasons why functional programming languages are more difficult than the rest, and how that prevents them from gaining wider adoption.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-hHG3-MPk0YkQh4_LrhziQ.jpeg" /><figcaption>Climbing Mount Lambda</figcaption></figure><p>During 2024’s <a href="https://adventofcode.com/">Advent of Code</a>, I <a href="https://github.com/surferjeff/scratch/tree/main/advent-2024">completed </a>39 of the 50 puzzles using <a href="https://fsharp.org/">F#</a>. F# describes itself as functional-first, and it’s really well suited for solving puzzles like these. It was fun and it was a great way to learn F#. My solutions were, on average, fewer lines of code than other solutions posted in other languages. I also found my F# solutions to be more readable, but I think every programmer finds their own code to be more readable.</p><p>However, even after weeks of practice with F#, I sometimes struggled to write code for reasons that were specific to functional programming languages, and this wasn’t my first time using a functional programming language.</p><p>The first computer science course I took in college in the 1990s was taught in <a href="https://www.scheme.org/">Scheme</a>. Scheme is a simpler version of <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp</a>. Lisp is the first functional programming language; it was first released in 1960.</p><p>In 2020, I started building a new web app, and I spent a few weeks evaluating <a href="https://elm-lang.org/">Elm</a>. Elm is a functional language that runs in the browser. I even <a href="https://github.com/surferjeff/surferjeff.github.io/blob/master/elm-hangman/src/Main.elm">wrote</a> a little <a href="https://surferjeff.github.io/elm-hangman/public/index.html">hang man game</a> using Elm. I ultimately chose other technologies to build <a href="https://www.pdfsnake.com/">my app</a>, but I liked Elm.</p><p>So at a minimum, Advent of Code was my third dive into a functional programming language. As I solved puzzles with F#, two reasons became apparent as to why functional programming languages are harder than the rest.</p><h3>Reason 1: Writing a loop is a lot harder.</h3><p>Loops are absolutely essential to code. The first line of code I ever wrote was 10 PRINT “JEFF IS KING” and the second line was 20 GOTO 10. So a loop was literally the second line of code I ever wrote. A programming language that makes it tricky to write a simple loop is going to struggle to find an audience.</p><p><a href="https://www.haskell.org/">Haskell</a>, <a href="https://www.roc-lang.org/">Roc</a>, <a href="https://elm-lang.org/">Elm</a>, <a href="https://clojure.org/">Clojure</a>, <a href="https://elixir-lang.org/">Elixir</a> and <a href="https://ocaml.org/">OCaml</a> don’t provide the familiar while and for loop language constructs that make writing loops easy in other languages. With these functional languages, you have two choices: recursion or “higher level” functions.</p><p>Recursion is hard. Don’t think it’s hard? Try explaining it to someone who has never written a line of code. Now try explaining loops. When I tried this experiment, my listener quickly understood loops but couldn’t grasp recursion. Still don’t think recursion is harder than writing a loop? Please leave a comment because you’re the first programmer I’ve met who thinks so. I must have written 300+ recursive functions over my 30 year career and I still find it easier to write loops. I like to avoid recursion when feasible, and in functional languages that means using higher level functions.</p><p><strong>To write loops without recursion in a functional programming language, you have to master about <em>50</em> higher level functions.</strong> Compare that to most other languages where you only have to master <em>2 or 3</em> language constructs (for, while, foreach) to write a loop.</p><p>Here are some higher level functions <a href="https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-seqmodule.html">provided by F#</a>. The ones in bold were used in my Advent of Code solutions.</p><p><strong>allPairs</strong>, average, averageBy, <strong>choose</strong>, chunkBySize, <strong>collect</strong>, compareWith, <strong>contains</strong>, countBy, except, <strong>exists</strong>, <strong>filter</strong>, find, findBack, findIndex, findIndexBack, <strong>fold</strong>, foldBack, forall, <strong>groupBy</strong>, countBy, <strong>iter</strong>, <strong>iteri</strong>, <strong>map</strong>, mapFold, mapFoldBack, <strong>mapi</strong>, max, maxBy, min, minBy, <strong>pairwise</strong>, pick, reduce, reduceBack, <strong>rev</strong>, <strong>scan</strong>, scanBack, skip, <strong>sum</strong>, <strong>sumBy</strong>, <strong>tryFind</strong>, tryFindBack, tryFindIndex, tryFindIndexBack, <strong>tryPick</strong>, <strong>unfold</strong>, where, windowed, zip.</p><p>You may want to ignore the higher level functions and stick with recursion, but that’s not practical. Chances are, your teammates will use higher level functions and you’ll need to read their code. And when your teammates review your code, they’ll ask you to replace your recursive functions with calls to higher level functions. It’s a reasonable request because code that calls higher level functions is usually easier to read than recursive functions. Also, code that calls higher level functions is usually a line or two shorter, and functional programmers love to play <a href="https://en.wikipedia.org/wiki/Code_golf">code golf</a>. So, you’re stuck having to learn about 50 functions compared to any non-functional language in which you’d have to learn only 3 looping constructs. Applying some questionable math, that makes functional programming languages 15 times more difficult to learn.</p><p>Even while programming solo, I found myself drawn towards higher level functions. Remarkably, my progression mirrored the <a href="https://en.wikipedia.org/wiki/Five_stages_of_grief">five stages of grief</a>:</p><p><strong>Stage 1: Denial</strong>. I denied the existence of recursion and higher level functions and kept writing for and while loops. F# is a bit unusual among functional programming languages because it provides for and while loop constructs, so I used them.</p><p><strong>Stage 2: Anger</strong>. F# provides for and while, but it doesn’t provide break, continue or return. This made writing loops frustrating. For example, on day 21, I wanted to write this code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c093b179f854bbeb6418f92aea29328b/href">https://medium.com/media/c093b179f854bbeb6418f92aea29328b/href</a></iframe><p>This code failed to compile because F# doesn’t have a return statement. To get the code to compile, I had to write:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/86b64be88c107186c5ca19652ac968d3/href">https://medium.com/media/86b64be88c107186c5ca19652ac968d3/href</a></iframe><p>I felt angry. I had to introduce additional variables and logic just to exit the loop, because F# didn’t provide a return statement. It felt like the designers of F# were intentionally making my life more difficult than it needed to be.</p><p><strong>Stage 3: Bargaining</strong>. I bargained with F# by switching from loops to recursion. With recursion, I could effectively break out of loops early.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6e4b29c5f5dd539da57c575978ab4dcb/href">https://medium.com/media/6e4b29c5f5dd539da57c575978ab4dcb/href</a></iframe><p><strong>Stage 4: Depression</strong>. I was disappointed that switching to recursion hadn’t provided any advantages. The recursive code avoids mutable state, and some people would call that a win, but I don’t, because my original function with a loop was already a <a href="https://en.wikipedia.org/wiki/Pure_function">pure function</a>. The recursive code is no fewer lines of code than the while loop above, so that didn’t feel like a win either.</p><p><strong>Stage 5: Acceptance</strong>. I finally concluded that to get the most out of F#, I’d have to take the time to study and learn 50 higher level functions.</p><p>Even after practicing F# for over 60 hours, and taking time to study every higher level function, it still took me 20 minutes to transform moveIsLegal into code that calls a higher level function:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8fdf093d0d8db3cf7f7c5d1f3b1aa2d6/href">https://medium.com/media/8fdf093d0d8db3cf7f7c5d1f3b1aa2d6/href</a></iframe><p>And this, far more than the other reason I discuss below, is why people struggle with functional programming languages. It took me a total of 30 minutes to write moveIsLegal in functional form compared to 10 minutes in procedural form. This was day 21 in the Advent of Code. I had spent at least 60 hours practicing F# and I was still struggling to write a loop.</p><h3>Reason 2: Misleading error messages.</h3><p>When I made mistakes writing F#, it was very hard to understand the compiler’s error messages. The error messages weren’t wrong, they just weren’t helpful, and sometimes they were even misleading. My experience was similar with Elm in 2020, and I believe it would be similar with Haskell, Roc, and OCaml, because they also implement <a href="https://en.wikipedia.org/wiki/Type_inference">type inferencing</a> and <a href="https://en.wikipedia.org/wiki/Currying">currying</a>.</p><p><strong>Type inferencing</strong> means that you rarely have to declare types of function arguments, function return values, or local variables, because the compiler figures out the types from context. When the code is correct, type inferencing is really nice because the code contains pure logic; it’s not cluttered with type information. However, when the code fails to compile, the compiler has fewer clues as to what the code is attempting to do, and therefore struggles to provide a useful error message. Here’s an example:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9b8fa22ca226886e836302804ea288a9/href">https://medium.com/media/9b8fa22ca226886e836302804ea288a9/href</a></iframe><p>What was my mistake? The comma on line 11 should be a semicolon. The 5 errors reported by the F# compiler gave me no clue to the true mistake. I frequently copied code into ChatGPT and asked it what was wrong. More often than not, ChatGPT helped. In this case, ChatGPT’s second guess provided the clue I needed to fix my mistake. But copying code into ChatGPT isn’t always an option. Many organizations wisely prohibit copying source code into ChatGPT. Even <a href="https://chatgpt.com/share/67721326-3024-8001-9df8-8d20b39b2c80">ChatGPT will tell you why</a> you shouldn’t copy code into ChatGPT.</p><p><strong>Currying</strong> is another language feature that is very nice when you get it right, and very difficult to figure out when you make a mistake. Imagine a function f that takes two arguments, f(a, b). What happens if you try to call f with only one argument f(a)? In most programming languages, the compiler will report an error. In languages that curry, compilers won’t report an error, at least not where you made the mistake. In languages that curry, f(a) returns another function that takes a single argument b. In other words let g = f(a); let n = g(b); evaluates to the same result as let n = f(a, b);. This means that when you accidentally pass too few arguments to a function, the compiler never reports an error at the call site. Instead, it reports an error somewhere else. Here’s another mistake I made:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b041f2f2727613303da10354b6035932/href">https://medium.com/media/b041f2f2727613303da10354b6035932/href</a></iframe><p>What was my mistake? I forgot to pass one more argument to Seq.scan on line 12. Could you deduce that from the error messages?</p><h3>Non-Conclusions</h3><p>I’m not claiming that functional programming languages are fundamentally flawed or unsuitable for any purpose. They have lots of great features found in few other languages. In my experience, when I write code in a functional language, I write fewer lines of code and fewer defects. If someone wanted to pay me to build a financial application with .NET, my first question would be, “Can I use F#?”</p><p>Also, I’m not claiming that other programming languages won’t find success by adding features borrowed from functional programming languages. Rust has immutable state and <a href="https://en.wikipedia.org/wiki/Tagged_union">tagged unions</a>. JavaScript arrays have methods like filter(), map(), and reduce(). Lots of books have been published with titles like <em>Functional Programming in C++/JavaScript/Go/etc</em>. These other languages don’t suffer from the 2 Reasons I described above, so importing functional language features won’t hurt their adoption.</p><h3>Conclusions</h3><p>I claim that functional programming languages are harder than the rest, and the additional level of difficulty will prevent them from becoming mainstream.</p><p>I also claim that functional language features arriving in procedural and object-orient languages won’t help functional languages become mainstream. I see the endpoint in the evolution of programming languages as a hybrid that weaves together the best ideas from all paradigms. I don’t see a march towards functional programming.</p><p>Maybe people who develop and maintain functional programming languages don’t want their languages to see greater adoption. But for those advocating greater adoption, I humbly offer the following advice.</p><p>First, build simple looping constructs into the language like for, while, foreach, and don’t forget continue, break, and return. If that means introducing mutable state in some limited way, then so be it. Functions with internal mutable state can still be pure.</p><p>Second, improve the compiler’s error messages. Maybe some AI built into the compiler would better diagnose mistakes and provide better error messages.</p><p>Third, I have heard a few high-profile speakers claim that a functional programming language is no more difficult than procedural programming languages. This message is counter productive. Here’s what happens when someone hears that message and tries a functional programming language for the first time: after struggling for a few hours, they feel dumb, and they distrust everything else you say. No one likes to feel dumb, and they resent you and your language for making them feel dumb. Their struggle has taught them functional programming is harder. You claimed it wasn’t. How many of your other claims are false? Do programmers really make fewer mistakes with functional languages, or is that just another false claim?</p><p>Instead of claiming functional programming languages aren’t more difficult, admit that they are more difficult, but show how the benefits outweigh the costs. The <a href="https://fsharp.org/">F# home page</a> gets this half right by focusing on the benefits, but nowhere does it prepare the reader for a long, frustrating learning journey.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1PSsrQtyc_GiVexBs9SAtw.jpeg" /><figcaption>Slow, rough road ahead.</figcaption></figure><h3>Final Doubts and Questions</h3><p>Maybe I’m wrong. Maybe a boom in the popularity of functional programming languages is right around the corner. But that begs the question, what makes tomorrow different from the past 65 years? If not for the extra difficulty with loops and compiler errors, then what exactly has kept functional programming languages from becoming mainstream? I look forward to reading your reasons in the comments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e1b5a8c9e4d8" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/why-functional-programming-languages-will-never-be-mainstream-e1b5a8c9e4d8">Why Functional Programming Languages Will Never Be Mainstream</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I wish Go were a Better Programming Language]]></title>
            <link>https://surferjeff.medium.com/i-wish-go-were-a-better-programming-language-e4aafe39f207?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/e4aafe39f207</guid>
            <category><![CDATA[go]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Sat, 02 Nov 2024 16:47:37 GMT</pubDate>
            <atom:updated>2025-02-21T17:21:38.803Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Shattered gopher statuette." src="https://cdn-images-1.medium.com/max/1024/1*qAtcrASwTuJJ-c6KgvTvFg.jpeg" /></figure><p>I recently wrote and deployed my first Go application.</p><p>Over a 30 year career, I’ve written applications in C, C++, Visual Basic, Objective-C, Typescript, Python, Tcl, PHP, Java, C#, Rust, and probably some other languages I’ve forgotten.</p><p>I just wrote and deployed my first Go application. It’s a little <a href="https://www.pdfsnake.com/tutorials/web-api.html">web service</a>. There’s not much to it: about 1000 lines of code that translate incoming HTTP requests into arguments for a command line tool, then pipe the output of the command line tool into HTTP responses. This is a perfect fit for Go, and I don’t regret choosing it. However, the experience made me wish Go were a better programming language.</p><p>I’ll start with the positive and proceed to the negative.</p><h3>The Positive</h3><p><strong>Go’s standard library is fantastic.</strong> With other languages like Typescript and Python, my apps have a few direct dependencies which in turn bring in hundreds of indirect dependencies. I probably spend about 10% of my coding time upgrading dependencies because a security vulnerability was discovered in an older version or the author decided to drop support for version 2 and everyone has to migrate to version 3. And I have never once run npm audit and seen a clean result.</p><p>By contrast, Go’s standard library provided a web server that supports HTTP2. That meant drastically fewer dependencies than same app built with Rust or Typescript. Also, I searched for Go’s vulnerability database and found that security vulnerabilities in the standard library are rare, and usually only deny service. These are way less scary than the vulnerabilities I routinely see in JavaScript dependencies.</p><p><a href="https://templ.guide/"><strong>Templ</strong></a><strong> is a dream</strong>. Why don’t all templating libraries work this way? I don’t need layouts, bodies, blocks, and the other confusing abstractions I see in other templating solutions. I’m looking at you, ASP.NET. All I need are type-safe functions and data. Templ succeeds in not making the solution more complicated than the problem.</p><p><strong>I love Goroutines</strong>. I’ve been a fan of light-weight threads since the 90s. They’re the best way to implement parallel processing. I’m mystified that languages born in the 90s and later like Java, Rust, and C# haven’t adopted them. Async Rust is a mess. Async Javascript is error prone. I’ve written Typescript code that forgot to await an asynchronous function, and the result was a long debugging session for me. With Goroutines, the flow of the code matches the flow of the logic in my head, and there’s no performance burden of an operating system thread.</p><p>Two more benefits of Go that were critical for my application were <a href="https://medium.com/@surferjeff/which-web-server-starts-up-the-fastest-java-python-c-nodejs-or-go-32a745983034">fast cold start times</a> and low memory usage. Go lived up to my expectations on all these points.</p><h3>The Surprisingly Neutral</h3><p>I’m surprised that I didn’t hate Go’s (lack of) error handling. Yes, roughly 1 of every 5 lines of my code is if nil != err {. However, delivering an accurate, useful error message to the user is always difficult, even with exceptions or Rust’s Result type.</p><p>Also, when I look at this code again in a few months, I suspect I’ll appreciate the if nil != err { statements in ways I don’t today. Error propagation and handling is difficult to understand in unfamiliar code written in a language that throws exceptions.</p><h3>The Negative</h3><p>During development, my application misbehaved and crashed in ways that would have been caught <em>at compile time</em> with other programming languages, specifically Typescript, Rust, and C#. I probably made other mistakes that I haven’t discovered yet and will eventually be discovered by my users. That’s a bad experience for them and me.</p><p><strong>I was burned by code-as-strings.</strong> Code-as-strings are string literals in the source code that actually behave as code.</p><p>I followed the official Go documentation and wrote this line of code: mux.HandleFunc(“GET /form”, formHandler) I was very surprised when a fetch for /form didn’t execute the formHandler. Instead, it executed the default handler, the same as fetching /. How could this be? After about an hour of debugging and searching, I learned that I had installed Go 1.21, but I was reading documentation for Go 1.23. HandleFunc() only started started supporting method specifiers (the GET in my string) in version 1.22.</p><p>I blame myself for mismatching the versions of the go compiler and the documentation. <strong>I blame the Go standard library authors for not catching my mistake at compile time.</strong> It would have been easy to catch it at compile time. Instead of enhancing the string passed to HandleFunc() in version 1.22, they could have added a new function like this: mux.HandleMethodFunc(“GET”, “/form”, formHandler) Then my error would have been caught at compile time, because the 1.21 compiler would have complained that HandleMethodFunc() doesn’t exist.</p><p>It’s nearly impossible for the compiler to catch mistakes in code-as-strings, and they’re used lots of places in Go, for example in serializing structs to Json:</p><pre>type LogEntry struct {<br> Severity string `json:&quot;severity&quot;`<br> Message  string `jsan:&quot;message&quot;`<br>}</pre><p>The Go compiler provides no clue that I have made a mistake in the code above. Other programming languages catch and report this mistake at compile time, because they don’t encode code as strings. They encode code as code.</p><p><strong>I dereferenced nil and Go panicked</strong>. <a href="https://en.wikipedia.org/wiki/Tony_Hoare">Tony Hoare called </a><a href="https://en.wikipedia.org/wiki/Tony_Hoare">null his billion-dollar mistake</a>. I guess the Go language designers wanted to one-up Mr. Hoare and invented nil. Not only did my code dereference nil on multiple occasions, I didn’t even understand when a value could be nil. Take this function, for example:</p><pre>func IsInternal(e HttpError) bool {<br>  ...</pre><p>Can e be nil? I can’t tell unless I go look at the definition of HttpError. If it’s a struct, then no, it can’t be nil. If it’s an interface, then yes, it can be nil.</p><p>If variable p is nil, can I call p.foo()? Maybe. It might panic, it might not. The only way to know is to check the definition of foo(). And as code is maintained, someone may change foo()’s definition so it doesn’t panic on nil, and leave behind a hundred call sites all checking if p == nil {. The ambiguity over whether a variable can be nil probably triggers more accidents than a broken traffic light.</p><p><strong>I also made mistakes with </strong><strong>nil that didn’t panic.</strong> I realized I had written a Go anti-pattern only after watching <a href="https://www.youtube.com/watch?v=ynoY2xz-F8s">Francesc Compoy’s talk on understanding nil</a> for the <em>second time</em>. I had written code like this:</p><pre>func OhNo() error {<br> var httperr *http_error = nil<br> return httperr<br>}<br><br>func TestOhNo(t *testing.T) {<br> if OhNo() == nil {<br>  fmt.Println((&quot;It&#39;s nil.&quot;))<br> } else {<br>  fmt.Println(&quot;No it isn&#39;t.&quot;)<br> }<br>}</pre><p>I was surprised when TestOhNo() printed “No it isn’t.” This is a language quirk that Go programmers learn quickly, I hope. I expect to make mistakes like this when learning a new language. <strong>But I blame Go for not catching this mistake at compile time.</strong> This bug was lurking in my code and I <em>got lucky</em> and watched Mr. Compoy’s talk and figured out my mistake. Otherwise, I would have only discovered it when a user reported my application behaving incorrectly.</p><p>By the way, go vet caught none of the issues I described above.</p><h3>Conclusion</h3><p>I make too many mistakes to make Go my first-choice programming language. I have come to depend on the Rust and Typescript compilers catching my mistakes. My users unknowingly appreciate it because they never see those mistakes. And one benefit I enjoy is writing fewer tests: no need to write a test for when a parameter is null if the parameter can never be null.</p><p>Even with this experience, there are still some, but not many applications for which I would choose Go. For nearly every programming language, I can imagine an application where it would be the ideal language to use.</p><p>But in the end, I feel disappointed by Go. The Go runtime is excellent. Go is so promising. I just wish Go were a better programming language. I wish it caught more mistakes at compile time instead of letting my users discover them at run time. Other languages do it; I expect no less from my next language.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e4aafe39f207" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[5 Reasons Why We Moved to Google Cloud Run from Firebase Functions]]></title>
            <link>https://surferjeff.medium.com/5-reasons-why-we-moved-to-google-cloud-run-from-firebase-functions-f6aec99a03f9?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/f6aec99a03f9</guid>
            <category><![CDATA[firebase-cloud-functions]]></category>
            <category><![CDATA[google-cloud-run]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Sun, 06 Oct 2024 16:16:54 GMT</pubDate>
            <atom:updated>2024-10-06T16:16:54.421Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="https://firebase.google.com/docs/functions">Firebase Functions</a> provide an easy path for programmers to quickly deploy a backend service for their websites. But as the website grows, it grows more difficult to maintain Firebase Functions. In this post, I break down our rationale for moving to Cloud Run into 5 reasons.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PELQ84zzu-w2_aQsmqSbCQ.png" /></figure><h3>1. Firebase Functions lack repeatable deploy and rollback.</h3><p>Deploying a Firebase Function means deploying JavaScript code, which in our case was transpiled from Typescript. Firebase functions don’t provide a way to archive the transpiled JavaScript code that we deployed.</p><p>When rolling back to an earlier version, deploying freshly re-transpiled JavaScript was risky. We had to check out the old source Typescript, recompile and redeploy, and hope that the build environment hadn’t changed since the code was first deployed. Perhaps we upgraded our version of npm or the Typescript compiler in the meantime and the code would fail to compile or behave differently. That’s bad.</p><p>A great feature of Firebase is that it allows you to deploy a subset of functions. If you only change the code for one function, you can redeploy just that one function.</p><p>A terrible flaw of Firebase is that it allows you to deploy a subset of functions. If you’re running 10 functions, they may have come from 10 different versions of the same code base or different code bases. It becomes very difficult to know exactly which code is running, and therefore which version to roll back to when there’s an issue.</p><p>We could build our own system to archive the JavaScript we had deployed, but <a href="https://cloud.google.com/run">Google Cloud Run</a> provides a ready-made solution. With Cloud Run, we’re running Docker images archived in <a href="https://cloud.google.com/artifact-registry/docs">Google Artifact Registry</a>. It’s trivial to switch back to an earlier Docker image, and we can control which Docker image is running with Terraform.</p><h3><strong>2. Firebase Functions lack certainty of runtime environment.</strong></h3><p>Exactly which version of NodeJs does a Firebase Function run in? What happens when Google decides to stop supporting NodeJs version Q and we’re still running it? What happens when they upgrade from version Q.01 to version Q.02?</p><p>We no longer have to worry about the answers to these questions because with Cloud Run, we choose and own the runtime environment inside the Docker image. Google can’t push us to the next version if we don’t want to move to it.</p><h3><strong>3. With Cloud Run, we can use any programming language.</strong></h3><p>Firebase Functions can only be written in NodeJs or Python. Cloud Run services can be written in any programming language, and invoke any binary that fits into a Docker image.</p><p>Here are some reasons to choose a stack besides NodeJs or Python:</p><ol><li>Your entire team are Ruby experts.</li><li>Performance is critical, so you need a faster programming language.</li><li>You need to manipulate images with <a href="https://imagemagick.org/index.php">ImageMagick</a>.</li></ol><p>In practice, all our functions are still running the original NodeJs code, but it’s nice to have these options if we need them.</p><h3><strong>4. With Cloud Run, there is less for attackers to exploit.</strong></h3><p>Firebase Functions’ service account has over 8000 privileges by default. Every unnecessary privilege granted to a service account gives an attacker more power if they find an exploit in your service.</p><p>Firebase Functions run as the App Engine default service account, and we found no way to change that. We could try removing privileges from that account one by one, but would other parts of our service break? I’m not sure.</p><p>Good security practice recommends starting from zero privileges and only adding them as necessary. With Cloud Run, we created a service account with zero privileges and added fewer than 10 privileges it needed to run correctly.</p><h3><strong>5. Migrating to Cloud Run wasn’t hard.</strong></h3><p>The Firebase client library invokes Firebase Functions via a simple HTTP protocol that took about 30 minutes to reverse engineer using only a web browser. It wasn’t hard to update the code running in Firebase Functions to run in a Cloud Run Service and speak the same protocol. It was even easier to update the client code to connect to a new host.</p><p>If you’d like to see a post about how the Firebase Functions protocol works and how to implement it, please leave a comment below.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f6aec99a03f9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Which web server starts up the fastest? Java, Python, C#, NodeJS, or Go?]]></title>
            <link>https://surferjeff.medium.com/which-web-server-starts-up-the-fastest-java-python-c-nodejs-or-go-32a745983034?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/32a745983034</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[go]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Fri, 10 May 2024 16:46:59 GMT</pubDate>
            <atom:updated>2024-05-10T16:46:59.621Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b1Tj9HIxUcogBMR6iW8aLA.jpeg" /></figure><p>An experiment measures how long 5 programming languages take to start up a web server.</p><p>When your website is too slow, users leave. Maybe the home page takes too long to load. Maybe the effects of a button click take too long to see. Either way, <a href="https://learninbound.com/blog/site-speed/">a three second delay may trigger users to abandon your website find another</a>, and your website makes less money.</p><p>Cold starts are one source of delay for web applications. A cold start for a web server refers to the process of initializing and launching the server from a state where it has not been running or is starting up from scratch. This typically involves tasks such as compiling source code or virtual machine code, establishing connections to databases or other services, and preparing to handle incoming requests from clients. During a cold start, the server may experience delays as it initializes, configures, and prepares itself to handle incoming traffic, which can impact response times for the first few requests.</p><p>Cold starts are uncommon when web applications run on persistent (virtual) machines like in Google Compute Engine, AWS EC2, or Azure Virtual Machines. With these platforms, cold starts happen only when a new version of the web application is deployed, or a machine is replaced or added to the pool of web servers. Otherwise, the web application may run for weeks without a restart, so very few users will experience a delay due to a cold start.</p><p>Cold start performance has grown more important with the adoption of serverless platforms like Google Cloud Run, AWS AppRunner, and Azure Container Apps. These platforms create and teardown instances of your web application in response to fluctuations in web traffic. The benefit is that you don’t pay for excess virtual machines when your website’s traffic is low. The drawback is many more users will experience delays due to cold starts, because the platforms frequently launch new instances of your web application when traffic increases.</p><p>Therefore, understanding how a programming language impacts your web application’s cold start time is more important than ever. It should never be the sole reason for choosing a programming language, but it’s essential data for making an informed decision.</p><p>We ran an experiment to measure cold start times of five different programming languages when run inside <a href="https://www.docker.com/">Docker</a> containers on <a href="https://cloud.google.com/run/">Google Cloud Run</a>. All the source code is available <a href="https://github.com/surferjeff/cold-start">here</a>.</p><p>Here are the measured response times for fetching a simple “Hello world” page from web servers built with the 5 languages. All times are in milliseconds.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b12be233bb3fb8d1b75fb70fe2ffae9b/href">https://medium.com/media/b12be233bb3fb8d1b75fb70fe2ffae9b/href</a></iframe><p>Go is the clear winner. Go’s response time at the 90th percentile is faster than all the other language’s 25th percentile. In this experiment, Go is more than 10 times faster than Java.</p><p>Surprisingly, the size of the docker image doesn’t seem to correlate with the cold start time. The two fastest languages, Go and NodeJS, have the smallest and largest docker images.</p><p>However, a “Hello world” application is of limited predictive value, because real web applications are much more complicated.</p><p>For the second experiment, we built more realistic web pages. The new web page runs a database query, and renders the result in the response. In this case, we used <a href="https://firebase.google.com/docs/firestore">Firestore</a> for the database. Running the database query entails loading the libraries to talk to the database, opening a connection to the database, authenticating, relaying the query, parsing the result, and rendering the result. This is much more similar to a real web application. Here are the results:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3866f3b0472e5f7449070a423735a0ce/href">https://medium.com/media/3866f3b0472e5f7449070a423735a0ce/href</a></iframe><p>Compared to the “Hello world” example, response times grew slower by about 50%, except for Go. Python and C# swapped positions, but otherwise the rankings stayed the same.</p><p>All the raw data is recorded in <a href="https://docs.google.com/spreadsheets/d/181ILKp7gMAij841Ib8k70xrPJK2MDhivdrAhmAwytnM/">this sheet</a>.</p><p>It’s worth repeating: performance in general and cold start times in particular should never be the sole reason to choose a programming language when building a web application. However, ignoring performance also comes with the risk of slow websites that turn users away.</p><p>I hope this post helps you make an informed decision when picking a programming language for you next web application.</p><p>Special thanks to <a href="https://www.linkedin.com/in/taiga-narusawa/">Taiga Narusawa</a> who wrote the code and collected the data for this experiment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=32a745983034" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why I Avoid Using Hash Tables]]></title>
            <link>https://surferjeff.medium.com/why-i-avoid-using-hash-tables-3bf5734fafb6?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/3bf5734fafb6</guid>
            <category><![CDATA[hashmap]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Tue, 05 Mar 2024 23:56:05 GMT</pubDate>
            <atom:updated>2025-04-14T16:49:40.875Z</atom:updated>
            <content:encoded><![CDATA[<p>Most of the programming languages I have used provide two dictionary types: one implemented with a <a href="https://en.wikipedia.org/wiki/Hash_table">hash table</a>, and another implemented with a <a href="https://en.wikipedia.org/wiki/Search_tree">search tree</a>. Here are some I’ve encountered:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8b8f44fb84f04d4dfdd962bb55959253/href">https://medium.com/media/8b8f44fb84f04d4dfdd962bb55959253/href</a></iframe><p>It’s well established that hash tables are faster, and all the evidence I’ve seen agrees. So you may be surprised to learn that when I write code, I avoid hash tables. Instead, I use search trees and I only switch to hash tables if I have evidence (a <a href="https://en.wikipedia.org/wiki/Profiling_(computer_programming)">profiler</a>’s report) that switching is necessary. My reasons follow.</p><h3>Hash tables summon chaos into your program.</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*LXixasLpO0tBvInN8r2C5g.jpeg" /></figure><p>Have you ever tried debugging an issue that happens 1 out of 100 tries? Or only happens in production, but not test environments? Or only happens on one machine that you don’t have access to? If you haven’t, then you must be new here; welcome to software engineering.</p><p>These rarely occurring issues are difficult and frustrating to track down and debug. They’re the result of <a href="https://en.wikipedia.org/wiki/Nondeterministic_algorithm">non-deterministic behavior</a>, which is a polite word for chaos.</p><p>There are lots of sources of chaos. Some examples are the computer’s clock, input and output, memory allocation patterns, and hash tables.</p><p>How do hash tables summon chaos? Consider the following code which populates two hash tables with the same Products, and then tests the hash tables for equality:</p><pre>record struct Product(int ProductId, string VendorName);<br><br>var a = new Dictionary&lt;Product, decimal&gt; {<br>    { new Product { ProductId = 1, VendorName = &quot;Contoso&quot; }, 100m },<br>    { new Product { ProductId = 2, VendorName = &quot;Fabrikam&quot; }, 150m }<br>};<br>var b = new Dictionary&lt;Product, decimal&gt; {<br>    { new Product { ProductId = 2, VendorName = &quot;Fabrikam&quot; }, 150m },<br>    { new Product { ProductId = 1, VendorName = &quot;Contoso&quot; }, 100m }<br>};<br><br>Console.WriteLine(&quot;.Equals() {0}&quot;, a.Equals(b));<br>Console.WriteLine(&quot;.SequenceEquals() {0}&quot;, a.SequenceEqual(b));</pre><p>What will this code print?</p><p>The correct answers are Line 1: False and Line 2: impossible to know.</p><p>.Equals() always returns False because C# is C#. It’s not what I hoped for, but at least it’s consistent.</p><p>.SequenceEquals() enumerates all the items in the dictionaries and compares each element. They may be enumerated in the same order, or they may not be. When they’re enumerated in the same order, the code will print True. When they’re not, the code will print False. The <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-8.0">docs</a> say:</p><blockquote>For purposes of enumeration, each item in the dictionary is treated as a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.keyvaluepair-2?view=net-8.0">KeyValuePair&lt;TKey,TValue&gt;</a> structure representing a value and its key. The order in which the items are returned is undefined.</blockquote><p>Undefined behavior is chaos. I don’t want chaos in my software. The better I can predict how my code will behave, the less time I’ll spend debugging it.</p><p>In practice, how does this chaos cause harm? Imagine I write a function and a test. The function dumps a hash table to a CSV file, and the test confirms the resulting CSV file looks as expected.</p><pre>static void WritePricesToCsv(Dictionary&lt;Product, decimal&gt; prices,<br>  string filename)<br>{<br>    using (var writer = new StreamWriter(filename))<br>    {<br>        foreach (var price in prices)<br>        {<br>            writer.WriteLine(&quot;{0},{1},{2}&quot;, price.Key.ProductId,<br>              price.Key.VendorName, price.Value);<br>        }<br>    }<br>}<br><br>[Test]<br>public void TestWritePricesToCsv()<br>{<br>    // Arrange<br>    var prices = new Dictionary&lt;Product, decimal&gt;<br>    {<br>        { new Product { ProductId = 1, VendorName = &quot;Vendor1&quot; }, 100m },<br>        { new Product { ProductId = 2, VendorName = &quot;Vendor2&quot; }, 200m }<br>    };<br>    var filename = &quot;test.csv&quot;;<br>    var expectedContent = &quot;1,Vendor1,100\r\n2,Vendor2,200\r\n&quot;;<br><br>    // Act<br>    WritePricesToCsv(prices, filename);<br><br>    // Assert<br>    var actualContent = File.ReadAllText(filename);<br>    Assert.AreEqual(expectedContent, actualContent);<br><br>    // Cleanup<br>    File.Delete(filename);<br>}</pre><p>I merge the code and the test passes for the next 10 years. Then I leave the project, some poor sap takes ownership of my code and the test starts failing because the foreach loop spontaneously starts listing the products in reverse order. It’s 100% allowed to do that because the documentation says the order of enumeration is undefined. To make matters worse, the test happens to fail only when the test runs on the <a href="https://en.wikipedia.org/wiki/CI/CD">CI/CD</a> machine. It always passes on the poor sap’s machine. It’ll probably take the poor sap a day to find the issue. What a frustrating waste of time.</p><p>So sometimes some tests fail. That’s not the end of the world. But it can get much worse. <strong>The next example is a lesson I learned the hard way.</strong></p><p>Imagine I’m building a microservice, and my microservice calls a backend Price Server periodically to fetch the latest prices.</p><p>The code looks something like:</p><pre>void RefreshPrices(Dictionary&lt;Product, decimal&gt; prices) {<br>    var request = new FetchPricesRequest();<br>    foreach (var price in prices) {<br>        request.Add(price.Key);<br>    }<br>    var response = FetchPrices(request);<br>    ...</pre><p>Now imagine there’s a million instances of my microservice all fetching requests from the same Price Server. Because the load is so heavy, I add a <a href="https://en.wikipedia.org/wiki/Proxy_server#Improving_performance">caching proxy </a>between my microservice and the Price Server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/723/1*G7LdpDTWmd3b-tvNzMoSRA.png" /></figure><p>The proxy serves 99% of the requests from its cache. If all the requests missed the cache and hit the Price Server directly, they’d overwhelm it.</p><p>Everything is running smoothly in production. Then, one day, for no apparent reason, the caching proxy which was serving 99% of requests from its cache instead starts serving 2% of requests from its cache and forwarding the other 98% of requests to the Price Server. The Price Server is overwhelmed and crashes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/723/1*teAcJhwQSgCE35G7pgL29w.png" /></figure><p>Why did the caching proxy suddenly fail to find requests in its cache? Because even though all the FetchPricesRequests contained the same Products, they contained them in different orders. To the proxy, requests with the Products in different orders are different requests. A request with products [a, b] can’t be served from a cache entry with products [b, a]. The proxy server must forward the request to the Prices Server.</p><p>Had I used a SortedDictionary (search tree) instead of Dictionary (hash table), the failures above would never have happened. Requests with the same lists of products would always carry those products in the same order, and therefore be served from the cache.</p><h3>Hash tables are vulnerable to denial of service (DoS) attacks.</h3><p>One type of DoS attack on hash tables is known as a “collision attack.”</p><p>Hash tables use a hash function to map keys to indices in an array (buckets). Ideally, each unique key should map to a unique index, but due to the finite size of the array and the practically infinite number of possible keys, collisions happen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/497/1*mNW02nCRmYbqbIvMSEIRAQ.png" /></figure><p>A collision happens when two or more keys are hashed to the same index. The hash table in the diagram above has a single collision at index 4 between the items C and D.</p><p>In the worst case scenario, all the keys hash to the same index. The performance of a hash table look up degrades from O(1) constant time to O(n) linear time. The hash table performs no better than a linear search in an array. The diagram below shows a hash table in this bad state.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/497/1*rptszOjzmiKquX4ZbHtkEQ.png" /></figure><p>An attacker attempting a DoS attack tries to get my hash table into this worst case scenario.</p><p>Imagine my web service allows customers to define new products. They provide the product name, and I keep the product information in a hash table with the product name as the key. An attacker can contrive a list of one million product names that all yield the same index when passed to my hash function. It will take some guess work for the attacker to figure out which hashing algorithm I use for the string. Lucky for the attacker, I used the default string hash function built into C#, which the attacker figures out very quickly. The attacker then begins adding one million products with colliding names to my web service. This causes my web service to burn 100% CPU doing linear searches and insertions into the hash table. My web service is too busy to serve my other customers’ requests. The DoS attack succeeds.</p><p>Had I chosen a search tree instead of a hash table, this attack would have had minimal effect on my service. The performance of a look up in a search tree never exceeds O(log(n)). My web service would have continued to serve other customers while the attacker defined a million new products.</p><h3>Hash tables leak memory.</h3><p>Hash tables leak memory when you remove items from them. To demonstrate, I wrote some code that fills a hash table with 100,000 items, then clears the table so it contains 0 items, and prints the size of heap memory after each step.</p><pre>void FillPrices(IDictionary&lt;Product, decimal&gt; prices)<br>{<br>    // Print the initial heap size.<br>    var startingMemory = GC.GetTotalMemory(true) / 1024;<br>    Console.WriteLine(&quot;initial memory: {0}&quot;, startingMemory);<br>    var now = DateTime.Now;<br>    <br>    // Fill the prices dictionary with 100,000 products.<br>    for (int i = 0; i &lt; 100000; i++)<br>    {<br>        prices.Add(new Product { ProductId = i, VendorName = RandomName() }, i);<br>    }<br><br>    // Print the heap size after filling the dictionary.<br>    var memory = GC.GetTotalMemory(true) / 1024;<br>    Console.WriteLine(&quot;memory for {0} items: +{1}&quot;, prices.Count,<br>        memory - startingMemory);<br><br>    // Clear the dictionary and print the heap size.<br>    prices.Clear();<br>    var elapsed = DateTime.Now - now;<br>    memory = GC.GetTotalMemory(true) / 1024;<br>    Console.WriteLine(&quot;memory for {0} items: +{1}&quot;,<br>        prices.Count, memory - startingMemory);<br><br>    // Refill it one more time and print the heap size.<br>    for (int i = 0; i &lt; 100000; i++)<br>    {<br>        prices.Add(new Product { ProductId = i, VendorName = RandomName() }, i);<br>    }<br>    memory = GC.GetTotalMemory(true) / 1024;<br>    Console.WriteLine(&quot;memory for {0} items: +{1}&quot;, prices.Count,<br>        memory - startingMemory);<br><br>    // Print the time it took to fill and clear the dictionary.<br>    Console.WriteLine(&quot;elapsed: {0}&quot;, elapsed);<br>}</pre><p>I ran the code and here’s the output:</p><pre>initial memory: 73<br>memory for 100000 items: +10658<br>memory for 0 items: +6752<br>memory for 100000 items: +10659<br>elapsed: 00:00:00.1015928</pre><p>I’m not surprised to see the hash table consumes 10 MB when it contains 100,000 items. I <em>am surprised</em> to see the hash table still consuming 6 MB when it contains 0 items. To be fair, the memory isn’t lost forever. It does get reused when more items are added to the Dictionary. But will my application actually refill the dictionary or just waste space with an empty dictionary? Answering that question requires a thorough investigation of all the code paths that touch the dictionary, and I’m lazy and I don’t want to do that.</p><p>Compare this to performing the same operations on a SortedDictionary (search tree) instead of a Dictionary:</p><pre>initial memory: 10732<br>memory for 100000 items: +10937<br>memory for 0 items: +0<br>memory for 100000 items: +10937<br>elapsed: 00:00:00.2382486</pre><p>After clearing the SortedDictionary, the garbage collector was able to reclaim all memory. I frequently work on <a href="https://webassembly.org/">web assembly</a> applications where memory is constrained, so SortedDictionary’s tidy memory habits are much more attractive to me.</p><p>Why do hash tables that contain 0 items consume so much memory? Earlier I described how hash tables use a hash function to map keys to indices in an array (buckets). As items are added to the hash table, buckets are added and the array grows. As items are removed from the hash table, buckets are dropped but the array never shrinks. In theory, the hash table could shrink the array, but in practice, I haven’t seen that happen. <a href="https://www.manning.com/books/100-go-mistakes-and-how-to-avoid-them">100 Go Mistakes and How to Avoid Them</a> specifically calls out this issue as #28. Have you seen a hash table actually shrink its array? Please leave a comment and tell me where.</p><h3>But hash tables are faster!</h3><p>Yes, they are, and the elapsed lines in the print outs above support that claim.</p><p>In the applications I’ve worked on, the slightly slower performance of a search tree has never been visible in a profiler. Something else, a compression algorithm for example, has always been burning up so much CPU that the performance of the search tree had no impact. Your mileage may vary.</p><h3>Conclusion</h3><p>If I’m trying to win a contest on <a href="https://leetcode.com/">leetcode.com</a>, I’ll choose a hash table because it’s faster. But if I’m writing code in any other context, I’ll choose a search tree so I can avoid the problems discussed above. A search tree always enumerates its items in sorted order, isn’t vulnerable to DoS attacks, and cleans up memory when items are removed.</p><p>There are ways to mitigate these problems when using a hash table, but those mitigations require diligence. I have a limited supply of diligence in a day, and I’d rather spend my diligence solving my customers’ problems than spend it solving problems created by my chosen data structure.</p><p>I hope this article helps you make more informed decisions when choosing between hash tables and search trees. If you think it will, please click the clap icon below.</p><p>The source code I used to measure memory and performance is <a href="https://github.com/surferjeff/dictionaries">available on GitHub</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3bf5734fafb6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Convert an ASP.NET Website into a SPA Using HTMX]]></title>
            <link>https://surferjeff.medium.com/convert-an-asp-net-website-into-a-spa-using-htmx-1274ae0d8be8?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/1274ae0d8be8</guid>
            <category><![CDATA[aspnet]]></category>
            <category><![CDATA[htmx]]></category>
            <category><![CDATA[single-page-applications]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Sat, 16 Sep 2023 20:11:55 GMT</pubDate>
            <atom:updated>2023-09-22T20:10:39.683Z</atom:updated>
            <content:encoded><![CDATA[<h3>Turn your ASP.NET MVC/Razor Website into a SPA Without Blazor</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fPqc1KthBOWGgT-gXAKCRA.png" /></figure><p>Should you rewrite your ASP.NET MVC/Razor website with Blazor? <strong>No</strong>. Blazor solves one problem and introduces three more. Blazor punishes your users with large, slow web-assembly packages and punishes users who have JavaScript disabled because your website becomes unusable. Blazor punishes you by requiring you to squander precious time rewriting your website and by making it much harder to debug production issues, because blazor websites rely on websockets or web assembly.</p><p>Instead of Blazor, save yourself time, money, and stress by using <a href="https://htmx.org/">HTMX</a> to transform your website into single-page application (SPA). With HTMX, you and your users will suffer none of the punishments of Blazor listed above.</p><p>In this article, I’ll demonstrate how to use HTMX to transform a legacy ASP.NET website into an interactive Single-Page Application by changing 10 lines of code, none of them JavaScript. I’ll describe the advantages of HTMX along the way.</p><p>This technique works for ASP.NET websites that follow the standard _Layout.cshtml file pattern, which looks something like this:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>&lt;head&gt;<br>    &lt;!-- meta tags --&gt;<br>    &lt;title&gt;@ViewData[&quot;Title&quot;] - Web Application&lt;/title&gt;<br>    &lt;!-- scripts --&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>    &lt;!-- navigation bar --&gt;<br>    &lt;div class=&quot;container&quot;&gt;<br>        &lt;main role=&quot;main&quot; class=&quot;pb-3&quot;&gt;<br>            @RenderBody()<br>        &lt;/main&gt;<br>    &lt;/div&gt;<br>    &lt;!-- footer and more scripts --&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p><strong>Sample Application</strong></p><p>To generate a sample ASP.NET website for this demonstration, I randotnet new webappwith the fictional business name <em>Wayoprise</em>. I used ASP.NET 6, but this technique works with older and newer versions of ASP.NET too. The source code is <a href="https://github.com/surferjeff/LegacySPA/tree/main5">on GitHub</a> and the website is <a href="https://legacy-spa-start-w4bahme6za-uc.a.run.app">running on Google Cloud Run</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ps6GFIbD8oVJYTrWSRHqXg.png" /></figure><p>When I visited the Wayoprise home page and clicked <strong>Privacy</strong>, the browser briefly blanked the screen and then loaded the Privacy page. It looked mostly the same as the previous Home page. The only thing that changed was about 100 bytes inside the &lt;main&gt; tag. However, I saw this in the browser’s network trace:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mpCWMah2tvqtKu483Q3Ggg.png" /></figure><p>This is a lot of wasted effort for a 100-byte change. For starters, 2.63kB was transferred, more than 10 times the size of the actual change. The browser didn’t download the JavaScript files because they were cached, but it still had to re-evaluate them in the context in new of the new page. This wasted time and energy on the server, and time and energy in the browser.</p><p><strong>My goal</strong> was to avoid all the wasted effort. When I clicked the <strong>Privacy</strong> link, I wanted to see the following network trace:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R-KwujuewfD0rkcUNTgnKQ.png" /></figure><p>Ideally, I wanted the bytes transferred to be only the new content inside the &lt;main&gt; tag:</p><pre>&lt;h1&gt;Privacy Policy&lt;/h1&gt;<br><br>&lt;p&gt;Use this page to detail your site&#39;s privacy policy.&lt;/p&gt;</pre><h3><strong>A Brief Introduction to HTMX</strong></h3><p><a href="https://htmx.org/">HTMX</a> is a lightweight (44k, 14k gzipped) JavaScript library that simplifies web development by enabling server-side features to be added to HTML elements with minimal code. It allows you to enhance traditional server-rendered web applications by adding client-side interactivity without the need for complex JavaScript frameworks. HTMX can progressively enhance existing HTML by specifying behavior via attributes, such as hx-get, hx-post, or hx-trigger, to fetch or send data to the server, update the DOM, and trigger events.</p><h3>Adding HTMX to the Sample Application</h3><p>To add HTMX to the application, I downloaded the HTMX Javascript library with the following command:</p><pre>curl -L &quot;https://unpkg.com/htmx.org@1.9.5&quot; &gt; wwwroot/lib/htmx.js </pre><p>And I added one line of code to _Layout.cshtml’s scripts section:</p><pre>  &lt;script src=&quot;~/lib/htmx.js&quot;&gt;&lt;/script&gt;</pre><p>And then HTMX was fully installed into my ASP.NET project. The changes were recorded in this <a href="https://github.com/surferjeff/LegacySPA/commit/4bcc6d4ad2c96434e55ea37de3482313055c0d94">git diff</a>.</p><h3>Updating the Sample Application to Use HTMX</h3><p>With HTMX installed, it was time to put it to work.</p><p>First, I wanted to tell HTMX, “when a user clicks a link in the body, don’t fetch a whole page. Instead, fetch a fragment of HTML and replace the contents of the &lt;main&gt; tag with the response.” Here’s how I did that.</p><p>I edited _Layout.cshtml and added hx-boost and hx-target to the body tag:</p><pre>&lt;body hx-boost=true hx-target=main&gt;  </pre><p>The <a href="https://htmx.org/attributes/hx-boost/">HTMX website</a> explains hx-boost:</p><blockquote>The <em>hx-boost</em> attribute allows you to “boost” normal anchors and form tags to use AJAX instead. This has the <a href="https://en.wikipedia.org/wiki/Progressive_enhancement">nice fallback</a> that, if the user does not have javascript enabled, the site will continue to work.</blockquote><blockquote>For anchor tags, clicking on the anchor will issue a <em>GET</em> request to the url specified in the <em>href</em> and will push the url so that a history entry is created.</blockquote><p>In the code sample above, hx-boost=true tells HTMX to boost all the links in the body. When a user clicks a boosted link, HTMX uses AJAX to fetch the URL. hx-target’s argument is any CSS selector. hx-target=main tells HTMX to replace the &lt;main&gt; tag with the response. Effectively, I told HTMX to replace the content returned by RenderBody() with the content returned by a new call to RenderBody().</p><p>Although I had told HTMX which element to fetch and replace, I hadn’t updated the application to return a <em>fragment</em> of HTML code. A fetch to /Privacy would still return a whole page.</p><p>So, I updated the top of _Layout.cshtml to return a fragment of HTML when the request was coming from an HTMX boosted link:</p><pre>@if (ViewContext.HttpContext.Request.Headers[&quot;HX-Boosted&quot;].Contains(&quot;true&quot;)) {<br>    &lt;text&gt;@RenderBody()&lt;/text&gt;<br>} else {<br>&lt;!DOCTYPE html&gt;<br>...</pre><p>HTMX adds an HX-Boosted: true header to its fetches. That made it easy to detect whether a request was coming from an HTMX boosted link or a regular link. With this code change, _Layout.cshtml responded with only the fragment of HTML returned byRenderBody()for HTMX-boosted links.</p><p>There was still one potential pitfall lurking in this code. A browser could cache the response of a request with HX-Boosted: true, and serve it from the cache for a request lacking HX-Boosted: true. That would result in a broken page. To tell the browser not to make that mistake, I added one more line of code:</p><pre> @{ ViewContext.HttpContext.Response.Headers.Add(&quot;Vary&quot;, &quot;HX-Boosted&quot;); }</pre><p>All these changes were recorded in <a href="https://github.com/surferjeff/LegacySPA/commit/17a65e33da779f80a05baf49efaf7766de82370c">this git diff</a>.</p><p>I compiled and ran the code, and the network trace looked exactly like what I wanted:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R-KwujuewfD0rkcUNTgnKQ.png" /></figure><p>No JavaScript or CSS was refetched or retrieved from the cache. Only the new HTML fragment was transferred.</p><p>However, I found a <strong>bug</strong>. After I clicked the privacy link, the url in the browser’s address bar was updated with the /Privacy path, but the title in the browser tab still said “Home page — Wayoprise” when it should have been updated to say “Privacy Policy — Wayoprise.”</p><p>Fortunately, HTMX provides a way to update the title at the same time it updates the body, by using the hx-swap-oob attribute. The <a href="https://htmx.org/attributes/hx-swap-oob/">HTMX website</a> describes hx-swap-oob:</p><blockquote>The hx-swap-oob attribute allows you to specify that some content in a response should be swapped into the DOM somewhere other than the target, that is “Out of Band”. This allows you to piggy back updates to other element updates on a response.</blockquote><p>To update the title, I inserted a new &lt;title&gt; tag into the fragment of code returned for HTMX boosted links:</p><pre>@if (ViewContext.HttpContext.Request.Headers[&quot;HX-Boosted&quot;].Contains(&quot;true&quot;)) {<br>    &lt;title hx-swap-oob=title&gt;@ViewData[&quot;Title&quot;] - Wayoprise&lt;/title&gt;<br>    &lt;text&gt;@RenderBody()&lt;/text&gt;<br>} else {<br>...</pre><p>All these changes were recorded in this <a href="https://github.com/surferjeff/LegacySPA/commit/2717b92074fc7bd3a6dc052fed02972d76de2a28">git diff</a>.</p><p>I recompiled and ran the website, and the HTMX-boosted web site rendered exactly like the original website. The response to a request for /Privacy looked like this:</p><pre>    &lt;title hx-swap-oob=title&gt;Privacy Policy - Wayoprise&lt;/title&gt;<br>&lt;h1&gt;Privacy Policy&lt;/h1&gt;<br><br>&lt;p&gt;Use this page to detail your site&#39;s privacy policy.&lt;/p&gt;</pre><p>The final result is <a href="https://legacy-spa-finish-w4bahme6za-uc.a.run.app/">running in Google Cloud</a>.</p><h3><strong>Success!</strong></h3><p>I converted this ASP.NET web site into a Single-Page application without writing a single line of JavaScript! Users now enjoy a faster, seamless website on every device, I’ve reduced my web hosting bills because my web servers handle fewer requests and transfer fewer bytes, and I have a code base that’s easier to understand than one built with Blazor or a JavaScript front-end framework. The source code for the sample is <a href="https://github.com/surferjeff/LegacySPA/tree/main5">posted to GitHub</a>.</p><p>HTMX is very powerful, and this article only began to explore the ways it can be applied to ASP.NET applications. Please clap if you found this article useful. Please clap twice if you’d like to see more examples of integrating HTMX with ASP.NET.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1274ae0d8be8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Other Reasons Why Rust Is My Favorite Programming Language]]></title>
            <link>https://itnext.io/other-reasons-why-rust-is-my-favorite-programming-language-ba6805a6a458?source=rss-8fbeff6da8a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/ba6805a6a458</guid>
            <category><![CDATA[rust]]></category>
            <dc:creator><![CDATA[Jeffrey Rennie]]></dc:creator>
            <pubDate>Thu, 24 Jun 2021 18:52:10 GMT</pubDate>
            <atom:updated>2021-06-24T18:52:10.205Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/621/1*OuDN901VJSSwAqK3mTtRnA.png" /></figure><p>There’s a lot of hype surrounding Rust now. Some features dominate the headlines:</p><ol><li>Rust is as fast as C or C++.</li><li>Rust is memory safe.</li></ol><p>Rust programmers also enjoy great documentation and a great package manager, which no doubt have contributed to Rust’s growing popularity.</p><p>There are a few more features of the Rust language that I thoroughly enjoy, but I haven’t seen discussed much. I’ve been coding in Rust for about a year, and in that time it has become my favorite language among the dozens I know.</p><p>Here are three less-talked-about language features that make programming in Rust a joy:</p><ol><li>There’s no null.</li><li>Powerful macros.</li><li>Precise control over mutability.</li></ol><p>There’s also a bonus feature I’m saving for the end, that only makes sense after explaining the other features.</p><p>The Rust <a href="https://cheats.rs/">cheat sheet</a> quickly explains some of Rust’s unique syntax in the code samples that follow.</p><p>Let’s dive deeper into each feature.</p><h3>There’s no null</h3><p>Null is probably the single greatest mistake in programming language design. <a href="https://en.wikipedia.org/wiki/Tony_Hoare">Tony Hoare called it his “billion-dollar mistake.”</a></p><p>Have you worked on a software project that never threw a NullPointerException or dereferenced null and crashed? If your project was written in Rust, Swift, Elm, or Haskell, then maybe you have. Otherwise, the chances are slim to null (pun intended).</p><p>Dereferencing null will bring a program to a screeching halt. To cope with the constant danger and avoid crashing their software, programmers write code like <a href="https://github.com/dotnet/aspnetcore/blob/52eff90fbcfca39b7eb58baad597df6a99a542b0/src/Components/Analyzers/src/ComponentFacts.cs#L15">this</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e11074565af20e5efeadc8c410700b60/href">https://medium.com/media/e11074565af20e5efeadc8c410700b60/href</a></iframe><p>This code is so dominated by null checks that it’s hard to understand what the function actually does. The poor programmer wrote more lines of code to cope with the possibility that an argument may be null than they wrote lines of code to solve the problem they wanted to solve. What’s more, programmers have read this null checking code so many times that they’ve trained themselves to ignore it. Did you see the bug I introduced in the code above?</p><p>Because Rust doesn’t have null, the sample code above could be written in Rust something like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a8b802dd02dea2c673512ae369157b6f/href">https://medium.com/media/a8b802dd02dea2c673512ae369157b6f/href</a></iframe><p>The Rust code doesn’t check for nulls because neither argument can be null. Any attempt to pass null would be caught by the compiler and reported as a type mismatch. Now that I know Rust, I cringe when I see null checking in other languages.</p><p>But of course, null can be very useful. Sometimes it’s convenient for classes or structures to optionally hold values, and for functions to optionally return values. For that purpose, Rust provides the <a href="https://doc.rust-lang.org/std/option/">Option&lt;T&gt;</a> type. An instance of Option&lt;T&gt; contains either None or Some(T) but never both.</p><p>Let’s consider a structure that represents a url like “<a href="https://www.google.com:80/">http://www.google.com</a>/”. Urls optionally contain a port number like this: “<a href="http://www.google.com:80/">http://www.google.com:80/</a>”. In Rust, we may represent a parsed Url with the following struct:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/67993576f1cf634aa15b90a87bb0a363/href">https://medium.com/media/67993576f1cf634aa15b90a87bb0a363/href</a></iframe><p>Imagine we want to write a method called incr_port() which increments the port number in a url; we’ll invoke the method like this url.incr_port();.</p><p>Naively, we might write the method like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2a075b9e9b69307cf0516a2a2df44317/href">https://medium.com/media/2a075b9e9b69307cf0516a2a2df44317/href</a></iframe><p>The Rust compiler rejects incr_port() above because port is not a u16; it’s an Option&lt;u16&gt;. The compiler prevents what would have been a latent null pointer exception in other languages.</p><pre>14 |      self.port += 1;<br>   |      ---------^^^^^<br>   |      |<br>   |      cannot use `+=` on type `std::option::Option&lt;u16&gt;`</pre><p>To increment the port, the code must first confirm that port has some value.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1c810af2d303f2ab4f0a26ead237fd85/href">https://medium.com/media/1c810af2d303f2ab4f0a26ead237fd85/href</a></iframe><p>With Option&lt;T&gt;, Rust provides optional values when we need them, without the possibility of crashing the program. More importantly, Rust saves the programmer from the chore of null checking every argument and object.</p><h3>Powerful macros</h3><p>Let’s continue with the Url struct above. I’ll add one line of code before the Url struct, and then discuss all the good things this one line does:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1220047433728f045ca119e82b8d9dff/href">https://medium.com/media/1220047433728f045ca119e82b8d9dff/href</a></iframe><p>By adding this one line of code,</p><p>#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash, Default, Debug)]</p><p>I can now compare two Urls for equality like this:</p><p>url == url2</p><p>I can compare them for ordering too. The PartialOrd &amp; Ord macros compare members of the struct in the order in which they appear in the struct.</p><p>url &gt; url2</p><p>Which means I can put Urls into sorted containers:</p><pre>let mut sorted_urls = BTreeSet::new();<br>sorted_urls.insert(url);</pre><p>I can clone a Url to get an exact copy:</p><p>let url2 = url.clone();</p><p>I can insert Urls into hash containers:</p><pre>let mut urls = HashSet::new();<br>urls.insert(url4);</pre><p>I can create a default Url, kind of like invoking a parameterless constructor in other languages:</p><p>let url = Url::default();</p><p>And, I can print a pretty debug string for a Url:</p><p>println!(“{:?}”, url);</p><p>Output: Url { protocol: “http”, host_name: “www.google.com&quot;, port: Some(80), path: “” }</p><p>That’s a ton of functionality to pack into one line code! In other languages, there are ways to implement the same behavior: code generators, post processors, IDEs, etc. Some languages use introspection. However, I like Rust’s solution best because no additional tools are needed, there’s no additional code for me or my peers to review and maintain, and type errors will be caught at compile time.</p><p>These macros all behave like you expect. For example Ord, short for ordering or ordered, compares each member of the struct in order. This may trip people up while refactoring code, because changing the order of the members of the struct will change the sort order. To avoid that, I could implement <a href="https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html">std::cmp::PartialOrd</a> and tailor it to my needs.</p><p>Third-party libraries can also take advantage of the #[derive()] macro. The <a href="https://crates.io/crates/serde">serde</a> library is Rust’s de facto standard for serializing/deserializing structs into/from more than a dozen formats. Let’s add some code to allow a Url to be serialized to and deserialized from json. After importing the serde library, I add two more attributes to the #[derive()] macro:</p><p>#[derive(…, Serialize, Deserialize)]</p><p>And now I can serialize and deserialize Urls from json.</p><pre>let text = serde_json::to_string(&amp;url)?;<br>let url2: Url = serde_json::from_str(&amp;text)?;</pre><p>Parse errors are immediately returned to the calling function via <a href="https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html">the </a><a href="https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html">? operator</a>.</p><p>The #[derive()] macro added a ton of functionality to my Url struct by adding only two symbols to my code! Had Url contained a type that could not be serialized to json, the compiler would have reported an error.</p><p><a href="https://en.wikipedia.org/wiki/With_great_power_comes_great_responsibility">With great power comes great responsibility</a>. If you maintained a C or C++ code base in the 1990s, then you probably saw people do terrible things with macros. The C/C++ preprocessor’s macros can replace any symbol with <em>any</em> text. People used that power to create code that looked nothing like C, and was very hard to understand and maintain. Here’s a real example from the era:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f88c751484cac1c251a7025db0cc235f/href">https://medium.com/media/f88c751484cac1c251a7025db0cc235f/href</a></iframe><p>This code actually defines a method, and builds an if-else ladder in the body, but you’d never know unless you dissected the macros. Examining this code in the debugger is also confusing. Here’s what BEGIN_MESSAGE_MAP() looks like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c4568960d8db81510332efbba7eaa483/href">https://medium.com/media/c4568960d8db81510332efbba7eaa483/href</a></iframe><p>Unsurprisingly, there was a backlash against macros for creating this mess. The languages born from this experience intentionally lacked macros: namely Java and Go.</p><p>But I think those languages <a href="https://en.wikipedia.org/wiki/Don%27t_throw_the_baby_out_with_the_bathwater">threw the baby out with the bath water</a>. The issue wasn’t macros in general; the issue was the C/C++ preprocessor. After using dozens of libraries in Rust, I have yet to see anything as mysterious as BEGIN_MESSAGE_MAP(). In fact, I’ve only used macros from two libraries: <a href="https://crates.io/crates/serde">serde</a> and <a href="https://docs.rs/anyhow/1.0.41/anyhow/index.html">anyhow</a>.</p><p>I see a few reasons why programmers have been more disciplined with macros in Rust than in other languages:</p><ol><li>Rust provides <a href="https://doc.rust-lang.org/reference/items/functions.html#const-functions">const functions</a>, which can replace macros in many cases. For example, you could write a const sqrt() function to calculate the square root of a constant at compile time.</li><li>With Rust macros, you can’t super-globally replace one symbol with any text you want. For example, this is impossible: #define PLUS +</li><li>In Rust, <a href="https://docs.rust-embedded.org/book/c-tips/index.html#macros">macros must expand to complete expressions, statements, items, types, or patterns</a>. They can’t expand to half a function. They can’t have unbalanced parenthesis, brackets or braces. The BEGIN_MESSAGE_MAP() macro above would be impossible in Rust because it has unbalanced braces.</li><li>Defining a macro in Rust is much more complicated than with the C/C++ preprocessor. Learning how to write a macro in Rust is like learning a whole new programming language. You can’t just #define max(a, b) ((a) &lt; (b) ? (b) : (a)). If something can be expressed any way besides a macro, Rust programmers like to choose the other way.</li></ol><h3>Precise Control over Mutability</h3><p>Rust gives the programmer precise control over mutability. Mutable data can be modified. Immutable data cannot be modified.</p><p>Immutable data is great for everyone who has to read code, be they human or machine. It’s far easier to reason about immutable data, it’s easier for the compiler to optimize code that uses immutable data, and immutable data is always thread safe.</p><p>Consider this sample code that uses the Url sample from above and demonstrates why immutable data makes code easier to reason about.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/00e25d7f508915b7a970ec018d93d241/href">https://medium.com/media/00e25d7f508915b7a970ec018d93d241/href</a></iframe><p>Because url is passed to fetch() via an immutable reference, it’s impossible for fetch() to modify the contents of url¹. Therefore, it’s easy to predict the value of url through each iteration of the loop. I don’t have to inspect fetch()’s code or even look at its argument types to confirm that it doesn’t modify url, because I can see the argument is passed as an <em>immutable</em> reference: &amp;url. To enable fetch() to modify url, I’d have to change the calling code to pass url as a mutable reference, like this: fetch(&amp;mut url).</p><p>At the same time, I don’t have to make url immutable in the context of the whole loop to make it immutable during the call to fetch(). I still can still modify url by calling url.incr_port() within the loop.</p><p>Passing url as an immutable reference also provides guarantees to fetch(). It’s not only impossible for fetch() to modify url, it’s impossible for anything else to modify url while fetch() has an immutable reference to url. No other thread can modify url while fetch() is executing. That makes writing fetch() easier.</p><p>I say that Rust gives the programmer <em>precise</em> control over mutability, because Rust doesn’t entangle mutability with other concepts. The programmer can choose for data to be mutable or immutable regardless of whether the data is allocated on the stack or on the heap, regardless of whether it’s passed by reference or by value, and regardless of whether it’s a struct or a primitive type like an integer.</p><p>Without precise control over mutability like Rust, other languages have introduced a variety of patterns and language features to provide some form of immutability. Some examples are the <a href="https://howtodoinjava.com/design-patterns/creational/builder-pattern-in-java/">builder pattern</a>, read-only interfaces, and multiple aggregate types each with their own mutability rules like class vs struct vs <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records">record</a>. Using these other solutions requires evaluating trade offs or writing more code than would be required in Rust.</p><h3>Bonus Feature: Rust is Expressive</h3><p>Rust has been described as an <em>expressive </em>language; expressive is a very abstract term. What is expressiveness and why is it useful?</p><p>In an expressive language, it’s possible to introduce new ideas that the language designers never dreamed of without changing the syntax of the language and without <a href="https://en.wikipedia.org/wiki/Boilerplate_code">boilerplate code</a>. The fact that many concepts were incorporated purely via a library rather than syntax is evidence that Rust is expressive.</p><p>For example, the Option&lt;T&gt; type is not built into the language; it’s just a standard library. Other languages required dedicated syntax to encode optional values, but with Rust, the existing language features allowed programmers to express optional values efficiently with Option&lt;T&gt;; no changes to the language were necessary.</p><p>Another example of Rust implementing a concept with a standard library rather than syntax is Rust’s <a href="https://doc.rust-lang.org/std/convert/trait.From.html">From&lt;T&gt; and Into&lt;T&gt;</a> traits, which allow programmers to write custom conversions from one type to another. Other languages added operator syntax to give programmers the ability to define custom type conversions. With Rust, the existing language features were sufficient. From&lt;T&gt; and Into&lt;T&gt; are ordinary traits.</p><p>This all sounds great for language designers, but how could it be practical for the rest of us?</p><p>Consider this function:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a5da6d0cf8b33150678fad4d52389d77/href">https://medium.com/media/a5da6d0cf8b33150678fad4d52389d77/href</a></iframe><p>The io::Result&lt;()&gt; return type can contain exactly one of the following: an Error value, or the empty value () signifying success.</p><p>Imagine implementing this function. To make sure we always reset the file pointer to its original position, it sure would be convenient to have something like try {} finally {} found in other programming languages. Then, we could simply call f.seek(original_offset) in the finally block. But Rust doesn’t have exceptions, and it doesn’t have try {} finally {}.</p><p>To cope with the absence of try {} finally {}, I wrote a macro that behaves the same way. First, some disclaimers:</p><ol><li><a href="https://github.com/surferjeff/surferjeff.github.io/blob/b7ad93f282ebc87f2b1c637b0aedbb32fc19e541/tryfinally/src/main.rs#L5">This</a> is the first macro I’ve ever written, so it surely can be improved.</li><li>This is the first macro I’ve ever written, because I haven’t had the need to write a macro.</li><li>This macro is for demonstration purposes only. When I actually had the same need in production code many months ago, I wrote plain Rust code.</li><li>Because Rust has a built-in macro called try!(), I had to name my macro something different, so I named it tryf!().</li></ol><p>Enough disclaimers. Here’s what the function implementation looks like with my tryf!() macro:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d6fd67a00b2bf3a707950bd44f9546e1/href">https://medium.com/media/d6fd67a00b2bf3a707950bd44f9546e1/href</a></iframe><p>The syntax is a bit different, but if you’re familiar with languages with exceptions, I hope it’s obvious what this code does. That’s the beauty of an expressive language. I can take ideas that are foreign to the language itself and express them efficiently in Rust.</p><p>Because Rust is expressive, I’m confident that I’ll be able to efficiently express a great variety of my own ideas in code, with mistakes caught at compile time, and without <a href="https://en.wikipedia.org/wiki/Boilerplate_code">boilerplate code</a>.</p><h3>Conclusion</h3><p>Of course, I also like Rust for the same reasons as so many others: similar speed to C/C++, memory safe, great docs, great package manager, runs in the browser via <a href="https://webassembly.org/">WebAssembly</a>, etc. But I thought Rust also had some hidden gems, so I wrote this post.</p><p>If you’re considering adopting Rust for work or hobby, I hope this post has given you some useful insights to help you make a more informed decision. To begin learning Rust, I recommend starting with the book <a href="https://doc.rust-lang.org/rust-by-example/">Rust by Example</a>.</p><h3>Acknowledgements</h3><p>Thank you to the following people who no doubt have different opinions about their favorite languages, but generously reviewed drafts of this post.</p><ul><li>The <a href="https://users.rust-lang.org/t/seeking-comments-on-my-draft-blog-post-about-my-favorite-features-of-rust/59230">Rust Forum</a></li><li><a href="https://codeblog.jonskeet.uk/">Jon Skeet</a></li><li><a href="https://www.linkedin.com/in/danielbankhead">Daniel Bankhead</a></li><li><a href="https://twitter.com/BenjaminCoe">Benjamin Coe</a></li></ul><p>Foot Notes</p><p>[1] Sometimes, you may need to add a reference count or the like to an otherwise immutable struct. For situations like these, Rust provides <a href="https://doc.rust-lang.org/std/cell/">Cell</a>, <a href="https://doc.rust-lang.org/std/rc/index.html">Rc</a>, and more.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ba6805a6a458" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/other-reasons-why-rust-is-my-favorite-programming-language-ba6805a6a458">Other Reasons Why Rust Is My Favorite Programming Language</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>