<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-09-21T03:31:51+00:00</updated><id>/feed.xml</id><title type="html">Notes to self</title><subtitle>Mike Treit&apos;s blog.</subtitle><entry><title type="html">Building the Crafting Interpreters repo on Windows</title><link href="/programming,/windows/2026/01/11/BuildingCraftingInterprersRepoOnWindows.html" rel="alternate" type="text/html" title="Building the Crafting Interpreters repo on Windows" /><published>2026-01-11T15:00:00+00:00</published><updated>2026-01-11T15:00:00+00:00</updated><id>/programming,/windows/2026/01/11/BuildingCraftingInterprersRepoOnWindows</id><content type="html" xml:base="/programming,/windows/2026/01/11/BuildingCraftingInterprersRepoOnWindows.html"><![CDATA[<p><img src="/images/CIBook.jpg" alt="Crafting Interpreters book cover" width="800" /></p>

<h1 id="crafting-interpreters">Crafting Interpreters</h1>
<p>I have been hearing about the book <a href="https://www.amazon.com/Crafting-Interpreters-Robert-Nystrom/dp/0990582930/">Crafting Interpreters</a>, by <a href="https://github.com/munificent">Robert Nystrom</a>, for a few years now. I have heard nothing but good things about it.</p>

<p>As someone who collects and reads many programming-related books, I decided to order a copy and check it out.</p>

<p>So far, a few chapters in, I am impressed. The book is written in a slightly whimsical style that, given the intimidating nature of the underlying subject matter, makes it much more approachable than it might be otherwise.</p>

<p>As a self-taught programmer who has spent most of their career working on large back-end server systems, I have no experience in things like writing compilers or interpreters. So I am rather looking forward to trying something outside of my usual comfort zone.</p>

<h2 id="trying-out-the-lox-language">Trying out the Lox language</h2>
<p>Chapter 3 is a tutorial of the Lox programming language, the language you will be creating interpreters for as you work through the book. As the author points out, it is not much fun to work through a tutorial if you cannot actually try the language, so he suggests starting by running his implementation at the <a href="https://craftinginterpreters.com/repo">Crafting Interpreters Repo</a></p>

<p>As the README for that repo points out, Robert does not use Windows and a few extra steps might be required to build the code on Windows rather than just executing his provided makefile.</p>

<p>For instance, the provided makefile uses POSIX commands like <code class="language-plaintext highlighter-rouge">find</code>, <code class="language-plaintext highlighter-rouge">rm</code>, and <code class="language-plaintext highlighter-rouge">printf</code> that don’t work directly on Windows.</p>

<p>The code in question is not very complicated or difficult to build, so instead we will just write some simple PowerShell scripts to take the place of the makefile.</p>

<p>I could have used <a href="https://learn.microsoft.com/en-us/windows/wsl/">Windows Subsystem for Linux (WSL)</a> but, well, I do not particularly enjoy programming on linux. I find the experience on Windows much more enjoyable.</p>

<p>So, without further ado, here is a brief guide to how I got things working.</p>

<h1 id="building-on-windows">Building on Windows</h1>
<p>It ended up not being particularly difficult, just a few steps to get the right tooling installed. I assume you are already using modern PowerShell (pwsh.exe) for the rest of this guide.</p>

<p>What follows covers building the clox (C) and jlox (Java) interpreters from the repo.</p>

<h2 id="prerequisites">Prerequisites</h2>
<p>First, fork <a href="https://craftinginterpreters.com/repo">the repo</a>.</p>

<p>Next, you will need the Dart SDK for running build scripts, a Java JDK for compiling Java code, and MinGW-w64 for the GCC C compiler.</p>

<h3 id="installing-required-tools-with-winget">Installing Required Tools with winget</h3>

<p>Open PowerShell (pwsh.exe) and run:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c"># Install Dart SDK</span><span class="w">
</span><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">Dart.Dart</span><span class="w"> </span><span class="nt">--silent</span><span class="w"> </span><span class="nt">--accept-package-agreements</span><span class="w"> </span><span class="nt">--accept-source-agreements</span><span class="w">

</span><span class="c"># Install Java JDK</span><span class="w">
</span><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">Oracle.JDK.23</span><span class="w"> </span><span class="nt">--silent</span><span class="w"> </span><span class="nt">--accept-package-agreements</span><span class="w"> </span><span class="nt">--accept-source-agreements</span><span class="w">

</span><span class="c"># Install MinGW-w64 (includes GCC and mingw32-make)</span><span class="w">
</span><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">BrechtSanders.WinLibs.POSIX.UCRT</span><span class="w"> </span><span class="nt">--silent</span><span class="w"> </span><span class="nt">--accept-package-agreements</span><span class="w"> </span><span class="nt">--accept-source-agreements</span><span class="w">
</span></code></pre></div></div>

<p>After installation, <strong>restart your PowerShell terminal</strong> to ensure <code class="language-plaintext highlighter-rouge">$env:PATH</code> changes are picked up.</p>

<h3 id="verify-installation">Verify Installation</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="n">dart</span><span class="w"> </span><span class="nt">--version</span><span class="w">
</span><span class="n">javac</span><span class="w"> </span><span class="nt">-version</span><span class="w">
</span><span class="n">gcc</span><span class="w"> </span><span class="nt">--version</span><span class="w">
</span><span class="n">mingw32-make</span><span class="w"> </span><span class="nt">--version</span><span class="w">
</span></code></pre></div></div>

<h2 id="building-the-interpreters">Building the Interpreters</h2>

<h3 id="update-dart-dependencies">Update Dart Dependencies</h3>

<p>The main repository, as of early 2026, uses older Dart dependencies that need updating for modern Dart versions. Edit <code class="language-plaintext highlighter-rouge">tool/pubspec.yaml</code> and update the environment and dependencies sections:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="na">environment</span><span class="pi">:</span>
  <span class="na">sdk</span><span class="pi">:</span> <span class="s1">'</span><span class="s">&gt;=2.12.0</span><span class="nv"> </span><span class="s">&lt;4.0.0'</span>
<span class="na">dependencies</span><span class="pi">:</span>
  <span class="na">args</span><span class="pi">:</span> <span class="s">^2.0.0</span>
  <span class="na">charcode</span><span class="pi">:</span> <span class="s">^1.3.0</span>
  <span class="na">glob</span><span class="pi">:</span> <span class="s">^2.0.0</span>
  <span class="na">image</span><span class="pi">:</span> <span class="s">^4.0.0</span>
  <span class="na">markdown</span><span class="pi">:</span> <span class="s">^7.0.0</span>
  <span class="na">mime_type</span><span class="pi">:</span> <span class="s">^1.0.0</span>
  <span class="na">mustache_template</span><span class="pi">:</span> <span class="s">^2.0.0</span>
  <span class="na">path</span><span class="pi">:</span> <span class="s">^1.8.0</span>
  <span class="na">pool</span><span class="pi">:</span> <span class="s">^1.5.0</span>
  <span class="na">sass</span><span class="pi">:</span> <span class="s">^1.70.0</span>
  <span class="na">shelf</span><span class="pi">:</span> <span class="s">^1.4.0</span>
  <span class="na">string_scanner</span><span class="pi">:</span> <span class="s">^1.2.0</span>
</code></pre></div></div>

<p>Then install dependencies:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="n">pushd</span><span class="w"> </span><span class="nx">tool</span><span class="w">
</span><span class="n">dart</span><span class="w"> </span><span class="nx">pub</span><span class="w"> </span><span class="nx">get</span><span class="w">
</span><span class="n">popd</span><span class="w">
</span></code></pre></div></div>

<h3 id="build-clox-the-c-interpreter">Build clox (the C Interpreter)</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c"># Create build directories</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nx">build</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build\release</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nx">build\release</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build\release\clox</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nx">build\release\clox</span><span class="w"> </span><span class="p">}</span><span class="w">

</span><span class="c"># Compile C source files</span><span class="w">
</span><span class="n">gcc</span><span class="w"> </span><span class="nt">-std</span><span class="o">=</span><span class="n">c99</span><span class="w"> </span><span class="nt">-Wall</span><span class="w"> </span><span class="nt">-Wextra</span><span class="w"> </span><span class="nt">-Werror</span><span class="w"> </span><span class="nt">-Wno-unused-parameter</span><span class="w"> </span><span class="nt">-O3</span><span class="w"> </span><span class="nt">-flto</span><span class="w"> </span><span class="nt">-c</span><span class="w"> </span><span class="nx">c\</span><span class="o">*.</span><span class="nf">c</span><span class="w">

</span><span class="c"># Move object files to build directory</span><span class="w">
</span><span class="n">Move-Item</span><span class="w"> </span><span class="o">*.</span><span class="nf">o</span><span class="w"> </span><span class="nx">build\release\clox\</span><span class="w">

</span><span class="c"># Link the executable</span><span class="w">
</span><span class="n">gcc</span><span class="w"> </span><span class="nt">-std</span><span class="o">=</span><span class="n">c99</span><span class="w"> </span><span class="nt">-Wall</span><span class="w"> </span><span class="nt">-Wextra</span><span class="w"> </span><span class="nt">-Werror</span><span class="w"> </span><span class="nt">-Wno-unused-parameter</span><span class="w"> </span><span class="nt">-O3</span><span class="w"> </span><span class="nt">-flto</span><span class="w"> </span><span class="nx">build\release\clox\</span><span class="o">*.</span><span class="nf">o</span><span class="w"> </span><span class="nt">-o</span><span class="w"> </span><span class="nx">build\clox.exe</span><span class="w">

</span><span class="c"># Copy to root for convenience</span><span class="w">
</span><span class="n">Copy-Item</span><span class="w"> </span><span class="nx">build\clox.exe</span><span class="w"> </span><span class="nx">clox.exe</span><span class="w">
</span></code></pre></div></div>

<h3 id="build-jlox-the-java-interpreter">Build jlox (the Java Interpreter)</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c"># Create build directories</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build\java</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build\java\com</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nx">build\java\com</span><span class="w"> </span><span class="p">}</span><span class="w">

</span><span class="c"># Compile the AST generator tool</span><span class="w">
</span><span class="n">javac</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">java</span><span class="w"> </span><span class="nt">-d</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">java\com\craftinginterpreters\tool\</span><span class="o">*.</span><span class="nf">java</span><span class="w">

</span><span class="c"># Run the AST generator to create generated source files</span><span class="w">
</span><span class="n">java</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">com.craftinginterpreters.tool.GenerateAst</span><span class="w"> </span><span class="nx">java\com\craftinginterpreters\lox</span><span class="w">

</span><span class="c"># Compile the jlox interpreter</span><span class="w">
</span><span class="n">javac</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">java</span><span class="w"> </span><span class="nt">-d</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">java\com\craftinginterpreters\lox\</span><span class="o">*.</span><span class="nf">java</span><span class="w">
</span></code></pre></div></div>

<h3 id="create-jloxps1-wrapper">Create jlox.ps1 Wrapper</h3>

<p>Create a file named <code class="language-plaintext highlighter-rouge">jlox.ps1</code> in the root directory with this content:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c"># Wrapper script to run jlox (Java interpreter)</span><span class="w">
</span><span class="n">java</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">com.craftinginterpreters.lox.Lox</span><span class="w"> </span><span class="bp">$args</span><span class="w">
</span></code></pre></div></div>

<h2 id="testing-the-interpreters">Testing the Interpreters</h2>

<h3 id="test-clox-c-interpreter">Test clox (C Interpreter)</h3>

<p>Run the interpreter in interactive mode:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="o">.</span><span class="n">\clox.exe</span><span class="w">
</span></code></pre></div></div>

<p>Try some Lox code:</p>

<pre><code class="language-lox">&gt; print "Hello from clox!";
Hello from clox!
&gt; var x = 10 + 5;
&gt; print x;
15
</code></pre>

<p>Press Ctrl+C to exit.</p>

<h3 id="test-jlox-java-interpreter">Test jlox (Java Interpreter)</h3>

<p>Run the interpreter in interactive mode:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="o">.</span><span class="n">\jlox.ps1</span><span class="w">
</span></code></pre></div></div>

<p>Try some Lox code:</p>

<pre><code class="language-lox">&gt; print "Hello from jlox!";
Hello from jlox!
&gt; var x = 10 + 5;
&gt; print x;
15
</code></pre>

<p>Press Ctrl+C to exit.</p>

<h3 id="run-test-files">Run Test Files</h3>

<p>You can also run Lox source files directly:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c"># Create a test file</span><span class="w">
</span><span class="sh">@'
print "Testing clox...";
var a = 1;
var b = 2;
print a + b;
'@</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-File</span><span class="w"> </span><span class="nt">-Encoding</span><span class="w"> </span><span class="nx">UTF8</span><span class="w"> </span><span class="nx">test.lox</span><span class="w">

</span><span class="c"># Run with clox</span><span class="w">
</span><span class="o">.</span><span class="n">\clox.exe</span><span class="w"> </span><span class="nx">test.lox</span><span class="w">

</span><span class="c"># Run with jlox</span><span class="w">
</span><span class="o">.</span><span class="n">\jlox.ps1</span><span class="w"> </span><span class="nx">test.lox</span><span class="w">
</span></code></pre></div></div>

<h2 id="notes">Notes</h2>
<p>For convenience, I have collapsed the build instructions into a single helper script (build.ps1):</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c"># Build clox</span><span class="w">
</span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"Building clox..."</span><span class="w"> </span><span class="nt">-ForegroundColor</span><span class="w"> </span><span class="nx">Green</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build\release\clox</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="nx">build\release\clox</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-Null</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="n">gcc</span><span class="w"> </span><span class="nt">-std</span><span class="o">=</span><span class="n">c99</span><span class="w"> </span><span class="nt">-Wall</span><span class="w"> </span><span class="nt">-Wextra</span><span class="w"> </span><span class="nt">-Werror</span><span class="w"> </span><span class="nt">-Wno-unused-parameter</span><span class="w"> </span><span class="nt">-O3</span><span class="w"> </span><span class="nt">-flto</span><span class="w"> </span><span class="nt">-c</span><span class="w"> </span><span class="nx">c\</span><span class="o">*.</span><span class="nf">c</span><span class="w">
</span><span class="nx">Move-Item</span><span class="w"> </span><span class="o">*.</span><span class="nf">o</span><span class="w"> </span><span class="nx">build\release\clox\</span><span class="w"> </span><span class="nt">-Force</span><span class="w">
</span><span class="n">gcc</span><span class="w"> </span><span class="nt">-std</span><span class="o">=</span><span class="n">c99</span><span class="w"> </span><span class="nt">-Wall</span><span class="w"> </span><span class="nt">-Wextra</span><span class="w"> </span><span class="nt">-Werror</span><span class="w"> </span><span class="nt">-Wno-unused-parameter</span><span class="w"> </span><span class="nt">-O3</span><span class="w"> </span><span class="nt">-flto</span><span class="w"> </span><span class="nx">build\release\clox\</span><span class="o">*.</span><span class="nf">o</span><span class="w"> </span><span class="nt">-o</span><span class="w"> </span><span class="nx">build\clox.exe</span><span class="w">
</span><span class="n">Copy-Item</span><span class="w"> </span><span class="nx">build\clox.exe</span><span class="w"> </span><span class="nx">clox.exe</span><span class="w"> </span><span class="nt">-Force</span><span class="w">

</span><span class="c"># Build jlox</span><span class="w">
</span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"Building jlox..."</span><span class="w"> </span><span class="nt">-ForegroundColor</span><span class="w"> </span><span class="nx">Green</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">Test-Path</span><span class="w"> </span><span class="nx">build\java\com</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mkdir</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="nx">build\java\com</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-Null</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="n">javac</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">java</span><span class="w"> </span><span class="nt">-d</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">java\com\craftinginterpreters\tool\</span><span class="o">*.</span><span class="nf">java</span><span class="w">
</span><span class="nx">java</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">com.craftinginterpreters.tool.GenerateAst</span><span class="w"> </span><span class="nx">java\com\craftinginterpreters\lox</span><span class="w">
</span><span class="n">javac</span><span class="w"> </span><span class="nt">-cp</span><span class="w"> </span><span class="nx">java</span><span class="w"> </span><span class="nt">-d</span><span class="w"> </span><span class="nx">build\java</span><span class="w"> </span><span class="nx">java\com\craftinginterpreters\lox\</span><span class="o">*.</span><span class="nf">java</span><span class="w">

</span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"Build complete!"</span><span class="w"> </span><span class="nt">-ForegroundColor</span><span class="w"> </span><span class="nx">Green</span><span class="w">
</span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"Run .\clox.exe or .\jlox.ps1 to start the interpreters"</span><span class="w">
</span></code></pre></div></div>

<p>Run with:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="o">.</span><span class="n">\build.ps1</span><span class="w">
</span></code></pre></div></div>

<h1 id="conclusion">Conclusion</h1>
<p>And there we have it. Now we can play around with the Lox programming language and get a feel for how it works, before moving on to the actual act of implementing our own interpreter for the language.</p>]]></content><author><name></name></author><category term="Programming," /><category term="Windows" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">New Windows Machine Setup - Cheat Sheet</title><link href="/programming,/windows/2026/01/05/NewMachine.html" rel="alternate" type="text/html" title="New Windows Machine Setup - Cheat Sheet" /><published>2026-01-05T10:00:00+00:00</published><updated>2026-01-05T10:00:00+00:00</updated><id>/programming,/windows/2026/01/05/NewMachine</id><content type="html" xml:base="/programming,/windows/2026/01/05/NewMachine.html"><![CDATA[<h1 id="the-right-tools-for-the-job">The right tools for the job</h1>
<p>Being productive as a programmer starts with having the right set of tools.</p>

<p>Here is my go-to list of tools I always install when setting up a new Windows machine.</p>

<div class="language-ps highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="nf">#</span> <span class="nf">CLI</span> <span class="nf">installs</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Git.Git</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">github.cli</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Microsoft.Edit</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">rustup</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">ripgrep</span> <span class="nf">-F</span> <span class="nf">pcre2</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">fastmod</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">bottom</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">tokei</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">hexyl</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">cmake</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">-e</span> <span class="nf">--id</span> <span class="nf">Microsoft.VisualStudio.BuildTools</span> <span class="nf">--override</span> <span class="nf">"--passive</span> <span class="nf">--wait</span> <span class="nf">--add</span> <span class="nf">Microsoft.VisualStudio.Workload.VCTools;includeRecommended"</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">starship</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">-e</span> <span class="nf">--id</span> <span class="nf">OpenJS.NodeJS.LTS</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">vscode</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Microsoft.PowerShell</span>
<span class="nf">Install-Module</span> <span class="nf">PSReadline</span> <span class="nf">#</span> <span class="nf">From</span> <span class="nf">pwsh</span> <span class="nf">window</span>
<span class="nf">cargo</span> <span class="nf">install</span> <span class="nf">zoxide</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Microsoft.DotNet.SDK.9</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Microsoft.DotNet.SDK.10</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">python</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">neovim</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">voidtools.everything</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">voidtools.Everything.Cli</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">-e</span> <span class="nf">Microsoft.Sysinternals.Suite</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">zoomit</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">KirillOsenkov.MSBuildStructuredLogViewer</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">-e</span> <span class="nf">--id</span> <span class="nf">Microsoft.AzureCLI</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">ShareX.ShareX</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">hxd</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">sharkdp.bat</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">chocolatey</span>
<span class="nf">choco</span> <span class="nf">install</span> <span class="nf">winfetch</span> <span class="nf">-f</span>
<span class="nf">choco</span> <span class="nf">install</span> <span class="nf">cascadiafonts</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Meld.Meld</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Nightingale</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">LINQPad.LINQPad.8</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">zed</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">fzf</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Microsoft.Azure.StorageExplorer</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">Bruno.Bruno</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">sharkdp.fd</span>
<span class="nf">npm</span> <span class="nf">install</span> <span class="nf">-g</span> <span class="nf">@usebruno</span><span class="nv">/cli</span>
<span class="nf">winget</span> <span class="nf">install</span> <span class="nf">GitHub.Copilot</span>

<span class="nf">#</span> <span class="nf">Non-CLI</span> <span class="nf">installs</span>
<span class="nf">cascadia</span> <span class="nf">code</span> <span class="nf">-</span> <span class="nf">https:</span><span class="err">/</span><span class="nv">/github.com/microsoft/cascadia-code/releases</span>

<span class="nf">#</span> <span class="nf">Get</span> <span class="nf">rid</span> <span class="nf">of</span> <span class="nf">the</span> <span class="nf">"modern"</span> <span class="nf">right-click</span> <span class="nf">menu:</span>
<span class="nf">reg.exe</span> <span class="nb">add</span> <span class="nf">"HKCU\Software\Classes\CLSID\</span><span class="p">{</span><span class="nf">86ca1aa0-34aa-4e8b-a509-50c905bae2a2</span><span class="p">}</span><span class="nf">\InprocServer32"</span> <span class="nv">/f</span> <span class="nv">/ve</span>

<span class="nf">#</span> <span class="nf">Get</span> <span class="nf">rid</span> <span class="nf">of</span> <span class="nf">"modern"</span> <span class="nf">notepad</span>
<span class="nf">Get-AppxPackage</span> <span class="nf">Microsoft.WindowsNotepad</span> <span class="nf">|</span> <span class="nf">Remove-AppxPackage</span>

<span class="nf">#</span> <span class="nf">Set</span> <span class="nf">Microsoft.Edit</span> <span class="s">(edit.exe)</span> <span class="nf">as</span> <span class="nf">the</span> <span class="nf">default</span> <span class="nf">git</span> <span class="nf">editor</span>
<span class="nf">git</span> <span class="nf">config</span> <span class="nf">--global</span> <span class="nf">core.editor</span> <span class="nf">edit.exe</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="Programming," /><category term="Windows" /><summary type="html"><![CDATA[The right tools for the job Being productive as a programmer starts with having the right set of tools.]]></summary></entry><entry><title type="html">The tragedy of ToList()</title><link href="/programming,/.net/2024/07/30/ToList.html" rel="alternate" type="text/html" title="The tragedy of ToList()" /><published>2024-07-30T10:00:00+00:00</published><updated>2024-07-30T10:00:00+00:00</updated><id>/programming,/.net/2024/07/30/ToList</id><content type="html" xml:base="/programming,/.net/2024/07/30/ToList.html"><![CDATA[<p>What do you see when you come across code like this?</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="n">docList</span><span class="p">.</span><span class="nf">OrderByDescending</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Score</span><span class="p">).</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">ForEach</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="c1">// Do something with item.</span>
<span class="p">});</span>
</code></pre></div></div>

<p>I see a tragic pit of failure for the ignorant, or callous indifference from those who should know better.</p>

<p>Either way, let’s discuss.</p>

<h2 id="a-bit-of-context">A bit of context</h2>

<blockquote>
  <p>C programmers think memory management is too important to be left to the computer. LISP programmers think memory management is too important to be left to the user.</p>

  <p>– <cite>Ellis and Stroustrup, The Annotated C++ Reference Manual</cite></p>
</blockquote>

<p>Automatic garbage collection (GC) in programming languages is a beautiful thing. It frees the programmer to focus on the essence of the problem they are trying to solve, not the minutiae of how the computer hardware actually implements the solution. This is especially true when applying functional programming techniques, where the declarative style of such programs makes <strong>how</strong> the computation is achieved unimportant compared to <strong>what</strong> it is achieving.</p>

<p>Newer programmers may have an incorrect impression that GC is a new, modern technique, designed to reduce the longstanding burden of manual memory management imposed by classical languages such as C and C++. This is entirely untrue, of course, as anyone who has studied computing history will know: one of the very first high-level programming languages, predating C by more than a decade, was John McCarthy’s <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)">LISP</a>, a functional language that introduced the technique of garbage collection to the world. This was circa 1959, at the dawn of computing, a date so long ago that the modern generation can likely hardly fathom it.</p>

<p>GC makes a bargain with the programmer: leave the memory management to me, at the cost of losing complete control over every aspect of how your program is executed, including things like when memory is freed. This, consequently, means accepting some possible performance overhead and inefficiency in exchange for increased simplicity and correctness. Memory safety and correctness issues are historically one of the greatest sources of bugs in computer software, so in a very large percentage of cases this is a highly acceptable tradeoff.</p>

<p>Even in the world of systems programming, where the mere thought of GC is anathema to the extreme performance requirements and low level control needed in something like your operating system kernel, there is a push towards memory-safe languages like <a href="https://www.rust-lang.org/">Rust</a>. In <a href="https://x.com/markrussinovich/status/1571995117233504257">a famous tweet</a>, Mark Russinovich declared that we should consider traditional languages like C/C++ deprecated for new projects.</p>

<p>.NET developers have the luxury of working in an ecosystem that is memory safe by default via an excellent Garbage Collector implementation, while also providing escape hatches (<code class="language-plaintext highlighter-rouge">unsafe</code>, <code class="language-plaintext highlighter-rouge">Span&lt;T&gt;</code>, etc.) to allow optimization in paths where the overhead of GC might be unacceptable.</p>

<h2 id="gc-quick-summary">GC Quick Summary</h2>
<p>The actual implementation of GC in .NET is a fairly complex topic; there are a lot of details related to different GC modes, various types of heaps, dealing with fragmentation and large objects, etc. I recommend reading the <a href="https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/">GC Documentation</a> for details.</p>

<p>We can, however, summarize a few salient points:</p>

<ol>
  <li>The vast majority of the types you define are likely to be classes and, thus, require GC.</li>
  <li>Application pauses and high CPU are some of the main performance issues in systems using garbage collection.</li>
  <li>The more garbage a program generates, the more work the GC has to do.</li>
</ol>

<h2 id="gc-abuse">GC Abuse</h2>
<blockquote>
  <p>It’s not the garbage collector that’s ruining your process. The garbage is ruining your process!</p>

  <p>– <cite>Bryan Cantrill, <a href="https://www.youtube.com/watch?v=HgtRAbE1nBM">Is It Time to Rewrite the Operating System in Rust?</a></cite></p>
</blockquote>

<p>The code snippet I opened this article with is tragic because it seems at first glance to so many programmers to be <em>entirely reasonable</em>. In fact, this kind of code is very common in my experience. This is even true in codebases where performance is critically important. Imagine a web service attempting to process hundreds or thousands of requests per-second, with minimal latency, filled with code like that.</p>

<p>In the minds of many programmers, more code means slower performance, less code means faster performance. Therefore, one-liners are superior! After all, how much code are we actually executing if it all fits on a single line?</p>

<p>These few innocuous tokens in a single line of code must be almost free, right?</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="nf">ToList</span><span class="p">().</span><span class="nf">ForEach</span><span class="p">(</span><span class="cm">/* ... */</span><span class="p">)</span>
</code></pre></div></div>

<p>Nothing could be further from the truth. The tragedy of <code class="language-plaintext highlighter-rouge">ToList</code> is that it seems so small, so innocent, a little flourish in the middle of a chain of calls to get us to the next item in the chain.</p>

<p>The issue, of course, is not about calling <code class="language-plaintext highlighter-rouge">ToList()</code> in and of itself. There are many reasons to take a sequence of things and turn them into a concrete list. Perhaps we want to perform a binary search, so we need the ability to access specific elements by index. Perhaps we need to make defensive copies before handing those copies to other threads, to avoid thread-safety issues. Perhaps we have a method that only accepts an <code class="language-plaintext highlighter-rouge">IList&lt;T&gt;</code> so we need to accommodate it. Or, perhaps we have an expensive <a href="https://learn.microsoft.com/en-us/dotnet/standard/linq/">LINQ</a> query that we want to ensure only executes exactly once. These and many more possible scenarios are completely acceptable reasons to turn our sequence into a List.</p>

<p>However, that is not what we have in that code snippet.</p>

<p>Consider what the above code does for a moment. It allocates a List for the <em>sole purpose</em> of calling the <code class="language-plaintext highlighter-rouge">ForEach</code> method, because that method only exists on the concrete <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> type. That’s right; unlike what you may have thought, the <code class="language-plaintext highlighter-rouge">ForEach</code> method is not part of the <code class="language-plaintext highlighter-rouge">IEnumerable</code> type (via LINQ), but is a one-off method defined on <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>.</p>

<p>To be blunt, that <code class="language-plaintext highlighter-rouge">ForEeach</code> method offends the senses. It should not exist.</p>

<p>Why? Because it offers no advantage over simply using a traditional <code class="language-plaintext highlighter-rouge">foreach</code> loop, other than being able to make it a satisfying ‘fluent’ one-liner. Which leads to many developers calling <code class="language-plaintext highlighter-rouge">ToList</code> <em>solely for the purpose of calling the <code class="language-plaintext highlighter-rouge">ForEach</code> method</em>!</p>

<p>For those of us who care about avoiding completely needless, excessive garbage on the managed heap, this is indeed a tragic misuse of an otherwise fine and upstanding method.</p>

<p>Think about it. We have some (potentially large) sequence of data in memory. It is most likely already backed in memory by some sort of data structure, such as a List, an Array, a HashSet, etc. Yet because we want to call the <code class="language-plaintext highlighter-rouge">ForEach</code> method, we make an entire copy of that data in memory, on the managed heap. Copying the array costs time, it costs CPU <strong>and</strong> it creates a bunch of additional work for the GC. All of this could be avoided by just doing the simple thing and writing a traditional foreach loop.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="kt">var</span> <span class="n">orderedList</span> <span class="p">=</span> <span class="n">docList</span><span class="p">.</span><span class="nf">OrderByDescending</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Score</span><span class="p">);</span>
<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">orderedList</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Do something with item.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here we have the sane version of this kind of code; no unnecessary copying of the entire list in memory, just a plain old-fashioned foreach loop.</p>

<p>Many developers wonder why LINQ does not provide a ForEach method. The answer is that it violates the philosophy of the functional nature of LINQ, which discourages side effects in favor of an input sequence always producing some result as output. The result can be another sequence or it can be a discrete value, but it always produces some output. In contrast, the <code class="language-plaintext highlighter-rouge">ForEach</code> method produces no output value at all; it is only about side effects.</p>

<p>Finally, if you are so enamored with one-liners and method chaining that you cannot live without the silly ForEach method, you can always trivially implement your own:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">internal</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">IEnumerableExtensions</span>
<span class="p">{</span>
    <span class="k">internal</span> <span class="k">static</span> <span class="k">void</span> <span class="n">ForEach</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="k">this</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">items</span><span class="p">,</span> <span class="n">Action</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">action</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">items</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nf">action</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At least that way you can avoid allocating unnecessary lists willy-nilly throughout your code.</p>

<h3 id="does-tolist-always-allocate-though">Does ToList always allocate though?</h3>
<p>Consider the following piece of code:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="n">List</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">myList</span> <span class="p">=</span> <span class="p">[</span><span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">]</span>
<span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="nf">AddFirstTwo</span><span class="p">(</span><span class="n">myList</span><span class="p">);</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>

<span class="kt">int</span> <span class="nf">AddFirstTwo</span><span class="p">(</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">values</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">list</span> <span class="p">=</span> <span class="n">values</span><span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
    <span class="k">return</span> <span class="n">list</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="p">+</span> <span class="n">list</span><span class="p">[</span><span class="m">1</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>While the <code class="language-plaintext highlighter-rouge">AddFirstTwo</code> method accepts any <code class="language-plaintext highlighter-rouge">IEnumerable&lt;int&gt;</code>, in this case the underlying type backing the <code class="language-plaintext highlighter-rouge">IEnumerable</code> is already a List! Isn’t the <code class="language-plaintext highlighter-rouge">ToList</code> method smart enough to notice this and simply return the underlying list without allocating any memory at all?</p>

<p>This is an unfortunate misconception that I have seen many developers voice. The answer is no, not at all.</p>

<p><code class="language-plaintext highlighter-rouge">ToList</code> must by definition make a new list from the underlying data. That is part of its implicit contract. The method itself is <a href="https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tolist?view=net-8.0">described as</a>:</p>

<blockquote>
  <p>Creates a List<T> from an IEnumerable<T>.</T></T></p>
</blockquote>

<p>Note the use of the word ‘Creates’. The method by definition always creates a new list, requiring that the contents are copied into that list from the underlying source. Any other behavior would break all sorts of code that relies on a copy being made, such as the defensive copying for thread safety scenario I mentioned earlier.</p>

<p>The same logic applies to <code class="language-plaintext highlighter-rouge">ToArray</code> as well.</p>

<h3 id="non-pessimization">Non-pessimization</h3>
<p><a href="https://caseymuratori.com/about">Casey Muratori</a> (known for his work in game engine design and optimization) did an excellent presentation some time back called <a href="https://youtu.be/pgoetgxecw8">Refterm Lecture Part 1 - Philosophies of Optimization</a> that I highly recommend watching.</p>

<p>(I’ve mentioned this talk before, in my post on <a href="https://treit.github.io/programming,/performance,/c%23/2021/12/10/FakeOptimization.html">Fake Optimization</a>)</p>

<p>I cite this talk often when I give presentations to other developers, as it introduced me to one of my now-favorite programming concepts: non-pessimization.</p>

<p>The idea is to avoid writing ‘pessimized’ code, where we can think of ‘pessimization’ as the opposite of ‘optimization’; that is, going out of your way to write code that makes the computer do more work than necessary, for no good reason whatsoever.</p>

<p>Calling <code class="language-plaintext highlighter-rouge">ToList().ForEach()</code> is a perfect example of pessimization in action.</p>

<p>Practice non-pessimization instead. Your garbage collector, and your users, will thank you.</p>

<p>Here is a very simple benchmark showing the difference between using a traditional foreach loop and calling <code class="language-plaintext highlighter-rouge">ToList().ForEach()</code> instead:</p>

<p><a href="https://github.com/Treit/MiscBenchmarks/blob/main/ForEachVsToListForEach/Benchmark.cs">ForEachVsToListForEach</a></p>

<p><img src="/images/2024-07-30/bench1.png" alt="bencmark results 1" /></p>

<p>Take particular note of the memory allocation ratio and think about the impact on the garbage collector.</p>

<h2 id="finding-low-hanging-fruit-using-regular-expressions">Finding low hanging fruit using regular expressions</h2>
<p>When I explore a codebase for the first time I must admit that one of the first things I do now is simply grep for <code class="language-plaintext highlighter-rouge">ToList</code> and see what pops up.</p>

<p>In fact, I have a magic regex that just finds bad code for me. (Should this be a Roslyn code analyzer instead of just a simple regex over the text of the source code? Probably!)</p>

<p>My preferred code search tool is <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> from the CLI, so my simple little technique is just this handy PowerShell function:</p>

<pre><code class="language-pwsh">function bad
{
    rg -i "\.(?:ToList|ToArray)\(\)\.(?!As|Json|Node\b|Index|CopyTo|FindLastIndex|AreMore)|Select\(. =&gt; .\)|(?:ToList|ToArray)\(\)\.Count &gt; 0" --pcre2 --glob !*test* --glob !*Test* --glob *.cs $args[0]
}
</code></pre>

<p>What this attempts to flag, among other things, is the pattern of including a call to <code class="language-plaintext highlighter-rouge">ToList()</code> or <code class="language-plaintext highlighter-rouge">ToArray()</code> in the <em>middle</em> of a chain of other calls. Very often this means we are allocating a list and then throwing it away, doing nothing of value except creating additional garbage for the GC to have to deal with.</p>

<p>I present the following real-world example I came across recently, which the above regex called out as suspicious:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">return</span> <span class="n">groupedItemsDictionary</span><span class="p">.</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">Select</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">ItemView</span> <span class="p">{</span> <span class="n">Id</span> <span class="p">=</span> <span class="n">x</span><span class="p">.</span><span class="n">Key</span><span class="p">,</span> <span class="n">Data</span> <span class="p">=</span> <span class="n">x</span><span class="p">.</span><span class="n">Value</span> <span class="p">}).</span><span class="nf">ToList</span><span class="p">();</span>
</code></pre></div></div>

<p>Did the sensible programmer in you immediately recoil, or did this code look normal to you?</p>

<p><code class="language-plaintext highlighter-rouge">ToList().Select().ToList()</code> is an anti-pattern. Any such use should immediately jump out as wrong.</p>

<p>Here are some more clear anti-patterns found in actual production code, and an example of how they could be re-written to be less offensive.</p>

<p>Bad 😔</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">items</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="n">d</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">Contains</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">);</span>
</code></pre></div></div>

<p>Better 😎</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">items</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">d</span> <span class="p">=&gt;</span> <span class="n">d</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">Contains</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Id</span><span class="p">);</span>
</code></pre></div></div>

<p>Again, don’t inject <code class="language-plaintext highlighter-rouge">ToList</code> in the middle of a method chain for no reason. What purpose does it serve other than to create garbage?</p>

<p>Bad 😔</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">if</span> <span class="p">(</span><span class="n">items</span><span class="p">?.</span><span class="nf">Where</span><span class="p">(</span><span class="n">_</span> <span class="p">=&gt;</span> <span class="n">_</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="n">_</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">indexId</span><span class="p">).</span><span class="nf">ToArray</span><span class="p">().</span><span class="n">Length</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Better 😎</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">if</span> <span class="p">(</span><span class="n">items</span><span class="p">?.</span><span class="nf">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="n">x</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">indexId</span><span class="p">).</span><span class="nf">Any</span><span class="p">())</span>
</code></pre></div></div>

<p>The original code for this last one really is quite terrible. Any time you see either <code class="language-plaintext highlighter-rouge">ToList().Length &gt; 0</code> or <code class="language-plaintext highlighter-rouge">ToArray().Length &gt; 0</code> or <code class="language-plaintext highlighter-rouge">someEnumerable.Count() &gt; 0</code> you immediately have to wonder why the <code class="language-plaintext highlighter-rouge">Any()</code> method was not used instead. <code class="language-plaintext highlighter-rouge">Any</code> requires only looking if there are any elements at all. The others require traversing the entire list.</p>

<p>(As a minor style point, using the discard character <code class="language-plaintext highlighter-rouge">_</code> for an actual variable name is also a rather awful choice for readability.)</p>

<p>You may see variants of this anti-pattern, like this one:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">if</span> <span class="p">(</span><span class="n">items</span><span class="p">?.</span><span class="nf">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="n">x</span><span class="p">.</span><span class="n">Id</span> <span class="p">==</span> <span class="n">indexId</span><span class="p">).</span><span class="nf">Count</span><span class="p">()</span> <span class="p">&gt;</span> <span class="m">0</span><span class="p">)</span>
</code></pre></div></div>

<p>Just on the off chance you were not yet convinced that this kind of code should be terminated with extreme prejudice, <a href="https://github.com/Treit/MiscBenchmarks/tree/main/ToArrayVsAny">here is a benchmark</a> of the above scenario.</p>

<p>The results speak for themselves:</p>

<p><img src="/images/2024-07-30/Any.png" alt="Any" /></p>

<p>Again, not just in terms of potentially much higher latency (47,000 times slower for the large list!) but also in terms of memory usage (again, in this case, 47,000 times more memory for the GC to have to eventually clean up).</p>

<h3 id="case-study---select-the-first-five-keys-and-return-them-as-a-list">Case study - select the first five keys and return them as a list</h3>
<p>Let’s examine another concrete example of a call that my “find bad code” regex flagged, once again in some code I was working in not that long ago:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="n">List</span><span class="p">&lt;</span><span class="n">KeyValuePair</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">,</span> <span class="kt">int</span><span class="p">&gt;&gt;</span> <span class="n">topicsList</span> <span class="p">=</span> <span class="n">topics</span><span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
<span class="n">topicsList</span><span class="p">.</span><span class="nf">RemoveAll</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Key</span> <span class="p">==</span> <span class="k">null</span><span class="p">);</span>
<span class="n">topicsList</span><span class="p">.</span><span class="nf">Sort</span><span class="p">((</span><span class="n">pair1</span><span class="p">,</span> <span class="n">pair2</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="n">pair2</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="nf">CompareTo</span><span class="p">(</span><span class="n">pair1</span><span class="p">.</span><span class="n">Value</span><span class="p">));</span>
<span class="kt">int</span> <span class="n">numOfTopics</span> <span class="p">=</span> <span class="n">topicsList</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span>
<span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">sortedTopics</span> <span class="p">=</span> <span class="n">topicsList</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">GetRange</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">Math</span><span class="p">.</span><span class="nf">Min</span><span class="p">(</span><span class="n">numOfTopics</span><span class="p">,</span> <span class="m">5</span><span class="p">));</span>
<span class="k">return</span> <span class="n">sortedTopics</span><span class="p">;</span>
</code></pre></div></div>

<p>Examine this code for a moment. What do you think? Would you call this out in a code review?</p>

<p>Let’s say that you do, commenting “This code seems inefficient. Do we really need to make a copy of the entire list here before returning?”</p>

<p>You get the reply: “The interface contract requires that we return a <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> here, so we don’t have a choice.”</p>

<p>Your response?</p>

<p>This code is an instructive microcosm of many similar small programming problems that come up in our day-to-day work all the time. We can distill the interesting part of this code (the part that calls <code class="language-plaintext highlighter-rouge">ToList</code>) into the following problem statement:</p>

<p><strong>Given a <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> of <code class="language-plaintext highlighter-rouge">KeyValuePair&lt;string, int&gt;</code>, take the first five items and return their keys as a <code class="language-plaintext highlighter-rouge">List&lt;string&gt;</code>.</strong></p>

<p>We will simplify by assuming the list in question has at least five elements, so we can eliminate having to worry about <code class="language-plaintext highlighter-rouge">Math.Min</code> for the sake of this discussion.</p>

<p>This seems like a quite simple programming problem and, honestly, it is!</p>

<p>However, not all solutions are created equal.</p>

<p>Let’s briefly sketch out five approaches to solving this:</p>

<h4 id="approach-1---select-tolist-getrange">Approach #1 - Select, ToList, GetRange</h4>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">return</span> <span class="n">_keyValuePairs</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">GetRange</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">5</span><span class="p">);</span>
</code></pre></div></div>

<p>This is the approach we flagged in the original code review.</p>

<h4 id="approach-2---getrange-select-tolist">Approach #2 - GetRange, Select, ToList</h4>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">return</span> <span class="n">_keyValuePairs</span><span class="p">.</span><span class="nf">GetRange</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">5</span><span class="p">).</span><span class="nf">Select</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">ToList</span><span class="p">();</span>
</code></pre></div></div>

<h4 id="approach-3---select-take-tolist">Approach #3 - Select, Take, ToList</h4>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">return</span> <span class="n">_keyValuePairs</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">Take</span><span class="p">(</span><span class="m">5</span><span class="p">).</span><span class="nf">ToList</span><span class="p">();</span>
</code></pre></div></div>

<h4 id="approach-4---take-select-tolist">Approach #4 - Take, Select, ToList</h4>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">return</span> <span class="n">_keyValuePairs</span><span class="p">.</span><span class="nf">Take</span><span class="p">(</span><span class="m">5</span><span class="p">).</span><span class="nf">Select</span><span class="p">(</span><span class="n">item</span> <span class="p">=&gt;</span> <span class="n">item</span><span class="p">.</span><span class="n">Key</span><span class="p">).</span><span class="nf">ToList</span><span class="p">();</span>
</code></pre></div></div>

<h4 id="approach-5---create-a-list-write-a-loop">Approach #5 - Create a list, Write a loop</h4>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;(</span><span class="m">5</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="m">5</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
<span class="p">{</span>
    <span class="n">result</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">_keyValuePairs</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">Key</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
</code></pre></div></div>

<p>(Not a one-liner! 👀)</p>

<h4 id="results">Results</h4>
<p><img src="/images/2024-07-30/bench3.png" alt="bencmark results 3" /></p>

<p>I would say that tells the tale.</p>

<p>The original answer our hypothetical developer defended in their code review is unfathomably bad compared to the alternatives. The execution time is exponentially worse and has similarly terrible allocation numbers, meaning once again that choosing the wrong approach can easily create a massive amount of extra work for the GC.</p>

<p>Looking at these results and putting just a little bit of thought into them, it’s not hard to understand why the original code is so bad. Calling <code class="language-plaintext highlighter-rouge">ToList()</code> immediately materializes the <em>entire list</em> into memory. So even though we have a lazily evaluated LINQ statement (the <code class="language-plaintext highlighter-rouge">Select</code>), that does us no good if the next call is to <code class="language-plaintext highlighter-rouge">ToList()</code>, which will copy the entire sequence into a new list in memory.</p>

<p>Again, this is the tragedy of such a simple, innocuous seeming method call. A distracted or inexperienced developer can easily look at this code and think “looks good!” without realizing the performance landmine lurking behind it.</p>

<p>The LINQ variants naturally benefit from lazy evaluation / deferred execution. The LINQ query is always known to limit the results to at most five entries before it is ever executed, so we never need to pay the expense of evaluating more than five items, even if the list is a million items long.</p>

<p>If you’re paying attention you might ask: why is approach #2 allocating so much more memory than approaches #3, #4 and #5? Ask yourself: how many lists does that approach allocate? How many do the others allocate?</p>

<p>This is the kind of thing you need to condition yourself to think about when writing any kind of code that calls <code class="language-plaintext highlighter-rouge">ToList</code> or similar methods. Consider what is going on behind the scenes and ask yourself if this might lead to excessive memory allocations or other performance issues.</p>

<p>Finally, we get to see that sometimes the old ways are the best ways; even though it might not be a sleek one-liner, writing a plain old-fashioned for-loop is still 3 to 5 times faster than even the best LINQ version, and allocates 2 to 3 times less memory.</p>

<p>One-liners are not always better.</p>

<h3 id="nothing-is-black-and-white">Nothing is black and white</h3>
<p>The following piece of code (taken from the Newtonsoft.json codebase) seems, at first glance, to be ludicrous.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Creates an array from an &lt;see cref="IEnumerable{T}"/&gt;.</span>
<span class="c1">/// &lt;/summary&gt;</span>

<span class="k">public</span> <span class="k">static</span> <span class="n">TSource</span><span class="p">[]</span> <span class="n">ToArray</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;(</span>
  <span class="k">this</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;</span> <span class="n">source</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IList</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;</span> <span class="n">ilist</span> <span class="p">=</span> <span class="n">source</span> <span class="k">as</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ilist</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">TSource</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TSource</span><span class="p">[</span><span class="n">ilist</span><span class="p">.</span><span class="n">Count</span><span class="p">];</span>
        <span class="n">ilist</span><span class="p">.</span><span class="nf">CopyTo</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">array</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">source</span><span class="p">.</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">ToArray</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Specifically, what is up with this?</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">return</span> <span class="n">source</span><span class="p">.</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">ToArray</span><span class="p">();</span>
</code></pre></div></div>

<p>The overall intent of the method seems clear enough: turn an <code class="language-plaintext highlighter-rouge">IEnumerable&lt;T&gt;</code> into an array. Why not, however, just call the existing <code class="language-plaintext highlighter-rouge">ToArray()</code> method that is part of LINQ?</p>

<p>Even if there is some justification for detecting an <code class="language-plaintext highlighter-rouge">IList&lt;T&gt;</code> and hand-writing the explicit array copy code (and it is not clear at first glance that such a justification exists), why have an extraneous <code class="language-plaintext highlighter-rouge">ToList()</code> before calling <code class="language-plaintext highlighter-rouge">ToArray()</code> in the subsequent code that handles types that are not <code class="language-plaintext highlighter-rouge">IList&lt;T&gt;</code>? Is this not committing the same sin we have been arguing against? Making an entire copy of the sequence in memory just to throw it away and allocate yet another copy by copying the List into an array?</p>

<p>Original sequence -&gt; copied into new List -&gt; copied into new Array.</p>

<p>Garbage everywhere!</p>

<p>Things are not always as straightforward as they seem, however. Consider a program that uses the above extension method:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="n">list</span> <span class="p">=</span> <span class="k">new</span> <span class="n">HashSet</span><span class="p">&lt;</span><span class="kt">int</span><span class="p">&gt;</span> <span class="p">{</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">5</span> <span class="p">};</span>
    <span class="kt">var</span> <span class="n">array</span> <span class="p">=</span> <span class="n">list</span><span class="p">.</span><span class="nf">ToArray</span><span class="p">();</span> <span class="c1">// Calling our dubious extension method 🫤</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="s">", "</span><span class="p">,</span> <span class="n">array</span><span class="p">));</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">static</span> <span class="n">TSource</span><span class="p">[]</span> <span class="n">ToArray</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;(</span>
    <span class="k">this</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;</span> <span class="n">source</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IList</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;</span> <span class="n">ilist</span> <span class="p">=</span> <span class="n">source</span> <span class="k">as</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">TSource</span><span class="p">&gt;;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ilist</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">TSource</span><span class="p">[]</span> <span class="n">array</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TSource</span><span class="p">[</span><span class="n">ilist</span><span class="p">.</span><span class="n">Count</span><span class="p">];</span>
        <span class="n">ilist</span><span class="p">.</span><span class="nf">CopyTo</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
        <span class="k">return</span> <span class="n">array</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">source</span><span class="p">.</span><span class="nf">ToList</span><span class="p">().</span><span class="nf">ToArray</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Running this program prints the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>❯ dotnet run
1, 2, 3, 4, 5
</code></pre></div></div>

<p>Great, let us remove that dirty, offensive call to <code class="language-plaintext highlighter-rouge">ToList</code> and run it again:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c1">// return source.ToList().ToArray();</span>
<span class="k">return</span> <span class="n">source</span><span class="p">.</span><span class="nf">ToArray</span><span class="p">();</span> <span class="c1">// Much better! 😎</span>
</code></pre></div></div>

<p>Running the program again produces the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>❯ dotnet run
Stack overflow.
Repeat 13771 times:
--------------------------------
   at Program.ToArray(System.Collections.Generic.IEnumerable`1&lt;Int32&gt;)
   at Program.Main(System.String[])
</code></pre></div></div>

<p>What?! Oh no! 😱</p>

<p>In our zealous efforts to remove an unnecessary <code class="language-plaintext highlighter-rouge">ToList</code> call, we introduced an incurable stack overflow bug!</p>

<p>What is happening here, of course, is that the <code class="language-plaintext highlighter-rouge">ToArray</code> that we are calling is not the one from System.Linq. It is actually the same one we just defined! If you look closely you will see that we have defined exactly the same method signature as the method from System.Linq, but because our method is in the same namespace as our class it takes precedence. This results in an infinite recursion and finally a stack overflow. 💀</p>

<p>In fact, it appears that this code exists in Newtonsoft.Json because it is intended to work with .NET Framework 2.0, where the <code class="language-plaintext highlighter-rouge">Enumerable</code> class (containing the various LINQ extension methods) did not even exist yet! It turns out that <code class="language-plaintext highlighter-rouge">Enumerable.ToArray()</code> does not exist in NETFX 2.0, but an explicit <code class="language-plaintext highlighter-rouge">List&lt;T&gt;.ToArray()</code> method, defined on the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> type itself, has existed since then. Calling <code class="language-plaintext highlighter-rouge">ToList()</code> here makes a <code class="language-plaintext highlighter-rouge">ToArray()</code> method available that will achieve the desired result without recursively stack-overflowing.</p>

<p>Lesson learned: even code that seems obviously wrong at first glance may be subject to subtle context that must be understood to make an informed decision about it. This is where a judicious comment explaining ‘why’ the code is the way it is can be invaluable.</p>

<h2 id="conclusion">Conclusion</h2>
<blockquote>
  <p>In the bad old days of computing, so the story goes, programmers were constrained by small machines, but those days are long gone.</p>

  <p>– <cite>Jon Bentley, Squeezing Space, <a href="https://www.amazon.com/Programming-Pearls-2nd-Jon-Bentley/dp/0201657880">Programming Pearls</a></cite></p>
</blockquote>

<p>As programmers in the modern world, we are awash in a glut of computer memory. Many of us hardly consider the implications of the code we write. The magic garbage collector takes care of everything and all we have to do is focus on solving the business problems at hand. You actually think saving memory, squeezing space, is a thing? Ok Grandpa, let’s get you to bed.</p>

<p>There was a time when computer memory was at a premium. When stepping back and thinking carefully about things like time / space tradeoffs was a critical part of the creative process of programming. When peeling back the curtain and trying, at least a little bit, to understand the implications of what is happening behind the scenes was a highly-valued skill.</p>

<p>In the hyperbolic title of this post, I called this the tragedy of <code class="language-plaintext highlighter-rouge">ToList</code>. The real tragedy, however, is the loss of awareness about these kinds of issues. Of seeing page after page of code, written by well-intentioned programmers, infested with completely unnecessary performance pitfalls due to a basic lack of critical thinking about what happens when you type those simple nine characters: <code class="language-plaintext highlighter-rouge">.ToList()</code>.</p>

<p>I just finished reading a book recently. It’s called <a href="https://www.amazon.com/Secret-Life-Programs-Understand-Computers/dp/1593279701">The Secret Life of Programs: Understand Computers - Craft Better Code</a></p>

<p><img src="/images/2024-07-30/SecretLifeOfPrograms.png" style="width:75%; height:75%;" /></p>

<p>It is an excellent book. The author is a curmudgeon in the best sense of the word, and his goal is to get newer programmers to understand not just the code they write, but how the computers that execute that code actually work. Armed with this knowledge, the individual can inform their act of writing code and create much better programs as a result.</p>

<p>This post is something of a small attempt to achieve the same thing. To encourage stepping back and really <em>thinking</em> about the code we write. Even if it is just a small and simple call to <code class="language-plaintext highlighter-rouge">ToList()</code> embedded in a much longer, more complex piece of code.</p>

<p>Nothing is ever that simple.</p>]]></content><author><name></name></author><category term="Programming," /><category term=".NET" /><summary type="html"><![CDATA[What do you see when you come across code like this?]]></summary></entry><entry><title type="html">Making my favorite interview question real</title><link href="/programming,/interviewing/2023/02/03/InterviewQuestion.html" rel="alternate" type="text/html" title="Making my favorite interview question real" /><published>2023-02-03T19:00:00+00:00</published><updated>2023-02-03T19:00:00+00:00</updated><id>/programming,/interviewing/2023/02/03/InterviewQuestion</id><content type="html" xml:base="/programming,/interviewing/2023/02/03/InterviewQuestion.html"><![CDATA[<p>Here is a variation of my favorite go-to interview question:</p>

<blockquote>
  <p>I hand you a drive containing two files.</p>

  <p>Each file is extremely simple: it consists of 32-bit integers, one after the other. So, the first four bytes is the first integer. The next four bytes is the second integer, and so on. The integers happen to be written in little-endian byte order.</p>

  <p>Your task is to produce a third file containing the distinct set of integers that are shared between the two input files. For example, if the value 100 happens to be in the first file but not the second file, it should not be included in the output file. If it happens to exist in both files, then it should be included in the output file.</p>

  <p>I need the result file by the end of the day. Other than that, I do not care at all how you produce it. Tell me how, if you were given this problem in the real world, you would <em>actually</em> create the result file.</p>
</blockquote>

<p>Now, there are many ways to approach this problem, which is one of the reasons I like this question.</p>

<p>However, there is a twist to this particular problem. If a candidate happens to come up with a nice solution, I give them a variation: what if the files are absolutely enormous? How would that change how you approach solving the problem?</p>

<p>While I have my personal favorite solutions for tackling this, in reality I never actually bothered to produce gigantic files and see how long it would actually take to solve. I knew solutions that should work in theory, but actually trying them was kind of left as an exercise.</p>

<p>Sharing this problem with other programmers, I’ve been asked a few times to actually give them the kind of input files I describe in the problem. They want to tackle it!</p>

<p>Well, ok then, let’s do it.</p>

<h2 id="update-10232025">Update: 10/23/2025</h2>
<p>In the original version of this blog post I included links to the actual files hosted in the cloud. Each file was 50GiB in size, for a total of 100GiB of data.</p>

<p>Due, however, to the length of time needed to download such files and the also not-entirely-zero egress cost I was paying, I have removed them.</p>

<p>Instead, I present a small C# program to generate the files locally.</p>

<p>The program takes three parameters: the file to generate, the size of the file in bytes to generate and a seed value.</p>

<p>Pick two seed values at random and use the following to generate your own huge files:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>dotnet run a.bin 53687091200 383
dotnet run b.bin 53687091200 980
</code></pre></div></div>

<h3 id="c-program-to-generate-the-input-files">C# Program to generate the input files</h3>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">using</span> <span class="nn">System.Buffers.Binary</span><span class="p">;</span>

<span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">Length</span> <span class="p">&lt;</span> <span class="m">2</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Usage:"</span><span class="p">);</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Program.exe &lt;filepath&gt; &lt;size&gt; &lt;seed&gt;"</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">var</span> <span class="n">filename</span> <span class="p">=</span> <span class="n">args</span><span class="p">[</span><span class="m">0</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(!</span><span class="kt">long</span><span class="p">.</span><span class="nf">TryParse</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">1</span><span class="p">],</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">size</span><span class="p">))</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Invalid input."</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">if</span> <span class="p">(!</span><span class="kt">int</span><span class="p">.</span><span class="nf">TryParse</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="m">2</span><span class="p">],</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">seed</span><span class="p">))</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Invalid input."</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">var</span> <span class="n">r</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Random</span><span class="p">(</span><span class="n">seed</span><span class="p">);</span>
<span class="k">using</span> <span class="nn">var</span> <span class="n">fs</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">FileStream</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">FileMode</span><span class="p">.</span><span class="n">Create</span><span class="p">);</span>

<span class="kt">var</span> <span class="n">buff</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="m">4</span><span class="p">];</span>

<span class="kt">var</span> <span class="n">buffer</span> <span class="p">=</span> <span class="n">size</span> <span class="p">&lt;</span> <span class="p">(</span><span class="m">1024</span> <span class="p">*</span> <span class="m">1024</span> <span class="p">*</span> <span class="m">1024</span><span class="p">)</span> <span class="p">?</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="n">size</span><span class="p">]</span> <span class="p">:</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="m">1024</span> <span class="p">*</span> <span class="m">1024</span> <span class="p">*</span> <span class="m">1024</span><span class="p">];</span>
<span class="kt">var</span> <span class="n">totalWritten</span> <span class="p">=</span> <span class="m">0L</span><span class="p">;</span>

<span class="k">while</span> <span class="p">(</span><span class="n">totalWritten</span> <span class="p">&lt;</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">r</span><span class="p">.</span><span class="nf">NextBytes</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
    <span class="kt">long</span> <span class="n">amountToWrite</span> <span class="p">=</span> <span class="n">size</span> <span class="p">-</span> <span class="n">totalWritten</span><span class="p">;</span>
    <span class="n">fs</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">Math</span><span class="p">.</span><span class="nf">Min</span><span class="p">(</span><span class="n">amountToWrite</span><span class="p">,</span> <span class="n">buffer</span><span class="p">.</span><span class="n">Length</span><span class="p">));</span>
    <span class="n">totalWritten</span> <span class="p">+=</span> <span class="n">buffer</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">totalWritten</span><span class="p">}</span><span class="s"> bytes written."</span><span class="p">);</span></code></pre></figure>

<p>If you want to take a stab at solving this problem, feel free to try to produce the third file. Remember that duplicates should be removed from the output, as we want the distinct set of shared values.</p>

<p>If you drop me a line telling me your two seeds and the size of the result file, I can tell you if you’re right 🙂.</p>

<p>Have fun!</p>]]></content><author><name></name></author><category term="Programming," /><category term="Interviewing" /><summary type="html"><![CDATA[Here is a variation of my favorite go-to interview question:]]></summary></entry><entry><title type="html">Fake Optimization</title><link href="/programming,/performance,/c%23/2021/12/10/FakeOptimization.html" rel="alternate" type="text/html" title="Fake Optimization" /><published>2021-12-10T13:00:00+00:00</published><updated>2021-12-10T13:00:00+00:00</updated><id>/programming,/performance,/c%23/2021/12/10/FakeOptimization</id><content type="html" xml:base="/programming,/performance,/c%23/2021/12/10/FakeOptimization.html"><![CDATA[<h2 id="the-start-an-online-discussion">The start: an online discussion</h2>
<p>I recently participated in an online conversation between a few C# programmers who were discussing the difference between <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/jagged-arrays">jagged arrays</a> and <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/multidimensional-arrays">multidimensional arrays</a> in C#.</p>

<p>The conversation went like this:</p>

<div style="font-family:Verdana;">
<b>Me</b>: Multi-dimensional arrays are contiguous in memory. Also, the sizes are fixed, whereas a jagged array the sizes of each element can be different.<br /><br />
<b>Programmer A</b>: Much faster to loop through an int[,] than int[][].<br /><br />
<b>Me</b>: You have a benchmark for that?<br /><br />
<b>Programmer B</b>: Does it really need one... like looping through something that is contiguous in memory would be faster than looping through something that's scattered all around...right?<br /><br />
<b>Me</b>: I don't doubt it is likely faster, but I'm an empiricist and when people say "much faster" I like to know "how much". But without a benchmark, I can't say.<br /><br />
<b>Programmer A</b>: I don't have a benchmark for it.<br /><br />
</div>

<h2 id="the-problem">The problem</h2>
<p>This short discussion reveals a problem that in some ways plagues the software industry. I have seen it many times in discussions with colleagues and other programmers.</p>

<p>So what’s the issue?</p>

<p>Step back and consider what we have here.</p>

<p>First, there is a flatly stated claim that it is “much faster” to use a multidimensional array over a jagged array.</p>

<p>Second, it is considered so obviously true to Programmer B that the idea of benchmarking it seems utterly unnecessary.</p>

<p>This is a picture perfect example of what <a href="https://twitter.com/cmuratori">Casey Muratori</a> calls “fake optimization” in his excellent talk on <a href="https://youtu.be/pgoetgxecw8">Philosophies of Optimization</a>.</p>

<p>Fake optimization is when someone makes a claim, such as “arrays are always faster than linked lists.” (it depends) or “for loops are faster than foreach loops in C#.” (<a href="https://github.com/Treit/MiscBenchmarks/tree/main/ArrayForVsForeach">they’re not</a>), without actually knowing what they are talking about.</p>

<p>The insidiousness of fake optimization is that it very often falls under the guise of conventional wisdom, things that “obviously” must be true, so programmers blindly accept it uncritically. I’ve been hearing the “for loops are faster than foreach loops” line for decades; perhaps it was true at one point. Actually measuring it myself, however, showed that this accepted “fact” was completely bogus, at least in the context of looping over arrays in C#.</p>

<p>The broader point is this: we as software developers, if we want to write high quality software that is fast, the kind of software that users actually <em>want to use</em>, must think critically. We must question our assumptions, constantly.</p>

<p>In other words, we need to embrace empiricism.</p>

<h2 id="empiricism">Empiricism</h2>
<p>Empiricism is a philosophy focusing on the idea that knowledge should be based on evidence, not a priori reasoning. It is one of the fundamental building blocks of the scientific method: theories that cannot be tested against real-world observations are inherently unscientific.</p>

<p>This idea should apply to computer programs as well as scientific theories.</p>

<p>In my experience, programmers who apply this philosophy to their work, emphasizing practicial experimentation and measurements over the purely theoretical, tend to build the best software. Such programmers are pragmatic. They are skeptical. They question their own assumptions and the assumptions of others. They keep everyone honest.</p>

<p>I have worked with some brilliant people who are not empiricists. Their approach to software is based more on theory, mathematical reasoning, beauty, perfectionism, ideal design ideas and patterns. These folks, brilliant though they are, do not write the highest quality software.</p>

<p>Perhaps it is better to let the theorists design the algorithms and prove the math, and let us grubby empiricists build the actual product.</p>

<p>I worked on an optimization problem once. The code in question did something like this:</p>

<ol>
  <li>Fetch a few hundred thousand rows from a remote database.</li>
  <li>Round any floating point columns to two decimal places.</li>
  <li>Apply a filter to the data in memory.</li>
  <li>Sort the data in memory.</li>
  <li>Return the appropriate page of results.</li>
</ol>

<p>The overall performance of the above sequence was unacceptably slow: it was taking seconds.</p>

<p>A fellow co-worker assumed the issue had to be in the database query since that was the only part that was doing anything that should have been remotely expensive and was the only part that was doing I/O. Everything else was pure in-memory operations, which should be incredibly fast, even on a few hundred thousands rows of data. They went to work on improving the database code.</p>

<p>Being an empiricist and not liking to make assumptions, I spent some time adding highly granular performance logging instrumentation to measure where all of the time was actually being spent inside the code.</p>

<p>It turns out that 90% of the time was in rounding the floating point values to two decimal places. That was the bottleneck.</p>

<p>(If you think that sounds crazy, we can talk about how terrible DataSet and DataTable are for performance over a beer some time.)</p>

<p>Empiricists go where the evidence leads, and sometimes it leads to surprising places.</p>

<h2 id="benchmarking">Benchmarking</h2>
<p><img src="/images/bdn1.jpg" style="width:50%; height:50%;float:right" />
Benchmarking is one of the great tools of the empiricist programmer. In the world of C# and .NET programming, we have the excellent <a href="https://benchmarkdotnet.org/">BenchmarkDotNet</a> tool to make it very easy to write high quality benchmarks.</p>

<p>I maintain a collection of microbenchmarks using BenchmarkDotNet at <a href="https://github.com/Treit/MiscBenchmarks">https://github.com/Treit/MiscBenchmarks</a>. These often arise from discussions with other programmers who practice fake optimization: they make a claim about performance, but they do not have the evidence to back it up. As an empiricist, I cannot help but want to see for myself what is actually true.</p>

<p>It is perhaps our greatest tool to defeat fake optimization.</p>

<p>So when Programmer A said that iterating multidimensional arrays is “much faster” than jagged arrays, I naturally wanted to see some evidence. It made sense to me that working with contiguous memory would indeed be faster, as it works better with CPU caches and prefetching and that kind of thing. I was curious how much faster.</p>

<p>So I wrote <a href="https://github.com/Treit/MiscBenchmarks/tree/main/MultiDimensionalVsJaggedArray">a benchmark</a>, of course.</p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">public</span> <span class="k">class</span> <span class="nc">Benchmark</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="kt">byte</span><span class="p">[,]</span> <span class="n">_mdim</span><span class="p">;</span>
    <span class="k">private</span> <span class="kt">byte</span><span class="p">[][]</span> <span class="n">_jagged</span><span class="p">;</span>

    <span class="p">[</span><span class="nf">Params</span><span class="p">(</span><span class="m">4</span><span class="p">,</span> <span class="m">10</span><span class="p">,</span> <span class="m">100</span><span class="p">,</span> <span class="m">1024</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">int</span> <span class="n">Size</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>

    <span class="p">[</span><span class="n">GlobalSetup</span><span class="p">]</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">GlobalSetup</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="n">_mdim</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="n">Size</span><span class="p">,</span> <span class="n">Size</span><span class="p">];</span>
        <span class="n">_jagged</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="n">Size</span><span class="p">][];</span>

        <span class="kt">var</span> <span class="n">r</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Random</span><span class="p">();</span>

        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">Size</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="n">_jagged</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="n">Size</span><span class="p">];</span>

            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">j</span> <span class="p">&lt;</span> <span class="n">Size</span><span class="p">;</span> <span class="n">j</span><span class="p">++)</span>
            <span class="p">{</span>
                <span class="kt">byte</span> <span class="n">b</span> <span class="p">=</span> <span class="p">(</span><span class="kt">byte</span><span class="p">)</span><span class="n">r</span><span class="p">.</span><span class="nf">Next</span><span class="p">(</span><span class="m">256</span><span class="p">);</span>
                <span class="n">_mdim</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">]</span> <span class="p">=</span> <span class="n">b</span><span class="p">;</span>
                <span class="n">_jagged</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="p">=</span> <span class="n">b</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="n">Benchmark</span><span class="p">]</span>
    <span class="k">public</span> <span class="kt">long</span> <span class="nf">SumJagged</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="kt">long</span> <span class="n">result</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">Size</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">j</span> <span class="p">&lt;</span> <span class="n">Size</span><span class="p">;</span> <span class="n">j</span><span class="p">++)</span>
            <span class="p">{</span>
                <span class="n">result</span> <span class="p">+=</span> <span class="n">_jagged</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">];</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="p">[</span><span class="nf">Benchmark</span><span class="p">(</span><span class="n">Baseline</span> <span class="p">=</span> <span class="k">true</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">long</span> <span class="nf">SumMultiDimensional</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="kt">long</span> <span class="n">result</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">Size</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
        <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">j</span> <span class="p">&lt;</span> <span class="n">Size</span><span class="p">;</span> <span class="n">j</span><span class="p">++)</span>
            <span class="p">{</span>
                <span class="n">result</span> <span class="p">+=</span> <span class="n">_mdim</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">];</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h2 id="results">Results</h2>

<table>
  <thead>
    <tr>
      <th>Method</th>
      <th>Size</th>
      <th style="text-align: right">Mean</th>
      <th style="text-align: right">Error</th>
      <th style="text-align: right">StdDev</th>
      <th style="text-align: right">Ratio</th>
      <th style="text-align: right">RatioSD</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>SumJagged</strong></td>
      <td><strong>4</strong></td>
      <td style="text-align: right"><strong>14.086 ns</strong></td>
      <td style="text-align: right"><strong>0.3162 ns</strong></td>
      <td style="text-align: right"><strong>0.5782 ns</strong></td>
      <td style="text-align: right"><strong>0.65</strong></td>
      <td style="text-align: right"><strong>0.04</strong></td>
    </tr>
    <tr>
      <td>SumMultiDimensional</td>
      <td>4</td>
      <td style="text-align: right">22.088 ns</td>
      <td style="text-align: right">0.4707 ns</td>
      <td style="text-align: right">0.6283 ns</td>
      <td style="text-align: right">1.00</td>
      <td style="text-align: right">0.00</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
    </tr>
    <tr>
      <td><strong>SumJagged</strong></td>
      <td><strong>10</strong></td>
      <td style="text-align: right"><strong>77.723 ns</strong></td>
      <td style="text-align: right"><strong>1.5904 ns</strong></td>
      <td style="text-align: right"><strong>3.5243 ns</strong></td>
      <td style="text-align: right"><strong>0.67</strong></td>
      <td style="text-align: right"><strong>0.04</strong></td>
    </tr>
    <tr>
      <td>SumMultiDimensional</td>
      <td>10</td>
      <td style="text-align: right">116.340 ns</td>
      <td style="text-align: right">2.3632 ns</td>
      <td style="text-align: right">5.4298 ns</td>
      <td style="text-align: right">1.00</td>
      <td style="text-align: right">0.00</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
    </tr>
    <tr>
      <td><strong>SumJagged</strong></td>
      <td><strong>100</strong></td>
      <td style="text-align: right"><strong>7,396.521 ns</strong></td>
      <td style="text-align: right"><strong>144.2413 ns</strong></td>
      <td style="text-align: right"><strong>236.9925 ns</strong></td>
      <td style="text-align: right"><strong>0.66</strong></td>
      <td style="text-align: right"><strong>0.02</strong></td>
    </tr>
    <tr>
      <td>SumMultiDimensional</td>
      <td>100</td>
      <td style="text-align: right">11,273.073 ns</td>
      <td style="text-align: right">220.4851 ns</td>
      <td style="text-align: right">301.8024 ns</td>
      <td style="text-align: right">1.00</td>
      <td style="text-align: right">0.00</td>
    </tr>
    <tr>
      <td> </td>
      <td> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
      <td style="text-align: right"> </td>
    </tr>
    <tr>
      <td><strong>SumJagged</strong></td>
      <td><strong>1024</strong></td>
      <td style="text-align: right"><strong>739,746.989 ns</strong></td>
      <td style="text-align: right"><strong>14,707.8503 ns</strong></td>
      <td style="text-align: right"><strong>28,686.5325 ns</strong></td>
      <td style="text-align: right"><strong>0.65</strong></td>
      <td style="text-align: right"><strong>0.04</strong></td>
    </tr>
    <tr>
      <td>SumMultiDimensional</td>
      <td>1024</td>
      <td style="text-align: right">1,133,884.866 ns</td>
      <td style="text-align: right">22,135.5315 ns</td>
      <td style="text-align: right">51,302.4602 ns</td>
      <td style="text-align: right">1.00</td>
      <td style="text-align: right">0.00</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>
<p>The original statement, that using multidimensional arrays would be “much faster”, while intuitively obvious to many programmers, was not just based on no actual evidence.</p>

<p>It was not just wrong.</p>

<p>It was spectacularly wrong.</p>

<p>The results in fact show the opposite: jagged arrays are faster.</p>

<p>That, my friends, is fake optimization.</p>]]></content><author><name></name></author><category term="Programming," /><category term="Performance," /><category term="C#" /><summary type="html"><![CDATA[The start: an online discussion I recently participated in an online conversation between a few C# programmers who were discussing the difference between jagged arrays and multidimensional arrays in C#.]]></summary></entry><entry><title type="html">Untangling AKS, Managed Identity and Key Vault</title><link href="/c%23,/azure,/kubernetes,/keyvault/2020/11/29/AksManagedIdentity.html" rel="alternate" type="text/html" title="Untangling AKS, Managed Identity and Key Vault" /><published>2020-11-29T16:19:00+00:00</published><updated>2020-11-29T16:19:00+00:00</updated><id>/c%23,/azure,/kubernetes,/keyvault/2020/11/29/AksManagedIdentity</id><content type="html" xml:base="/c%23,/azure,/kubernetes,/keyvault/2020/11/29/AksManagedIdentity.html"><![CDATA[<p><img src="/images/2020-11-29/tangle2.png" style="width:100%; height:100%;float:top" /></p>

<p>I collapsed into bed on a recent gloomy November evening feeling mentally exhausted. Hours had been spent reading online help. Perusing blog posts. Watching videos on YouTube. All of that, yet I didn’t seem any closer to solving my problem.</p>

<p>The scenario was at least conceptually simple:</p>

<ol>
  <li>I am writing a program that needs access to data stored in an <a href="https://azure.microsoft.com/en-us/services/key-vault/">Azure Key Vault</a>.</li>
  <li>The code, written in C#, is going to be running in a Linux-based container hosted in a <a href="https://kubernetes.io/">Kubernetes</a> cluster, via <a href="https://azure.microsoft.com/en-us/services/kubernetes-service/">Azure Kubernetes Service (AKS)</a>.</li>
  <li>I want the code to authenticate to KeyVault using an Azure <a href="https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview">Managed Identity</a>.</li>
</ol>

<p>That’s it.</p>

<p>Not complicated.</p>

<p>In fact, this is exactly the kind of scenario Managed Identity is designed for, so one would think that a guide showing exactly how to set this up would be one of the first things that turns up in an online search.</p>

<p>Exasperatingly, that was not my experience. While there is plentiful information out there on configuring Managed Identity for an AKS cluster, nothing I found walked through the complete end-to-end scenario where you start from scratch and end with code in an AKS cluster reading data successfully from Key Vault.</p>

<p>Before finally retiring for the night, I took one last stab at finding an answer: a Twitter search.</p>

<p>I typed into the Twitter search box on my phone: <em>AKS Managed Identity Key Vault</em></p>

<p>This resulted in one <a href="https://twitter.com/AzureSupport/status/1327219664427028481">interesting looking result</a>, posted by <a href="https://twitter.com/AzureSupport">Azure Support</a>:</p>

<p><img src="/images/2020-11-29/tweet1.png" style="width:50%; height:50%;float:top" /></p>

<p>…but when I navigated to their <a href="https://docs.microsoft.com/en-us/azure/aks/kubernetes-service-principal">guide</a>, I discovered it has nothing related to configuring Managed Identity! Hopes dashed, I somewhat grumpily replied to their tweet before drifting off to sleep:</p>

<p><img src="/images/2020-11-29/tweet2.png" style="width:50%; height:50%;float:top" /></p>

<h2 id="spoiler-alert---i-did-eventually-get-it-working">Spoiler alert - I did eventually get it working</h2>
<p>You can read the <a href="https://twitter.com/MikeTreit/status/1330048812119977989">Twitter thread</a>, but ultimately the extremely helpful <a href="https://twitter.com/ckittel">Chad Kittel (@ckittel)</a> came to my rescue and pointed me to a critical step I had been missing.</p>

<p>I had been following something called the <a href="https://azure.github.io/aad-pod-identity/docs/demo/standard_walkthrough/">Standard Walkthrough</a> on setting up AAD Pod Identity, a critical piece required to get Managed Identity working with an AKS cluster. My mistake was thinking that the instructions in that walkthrough were complete; they were not, and the linked document on <a href="https://azure.github.io/aad-pod-identity/docs/getting-started/role-assignment/">Role Assignment</a>, which the walkthrough mentions as a kind of aside, turned out to showcase a critical step. Chad pointed this out and that lead me to a successful solution.</p>

<h2 id="learning-amid-overwhelming-complexity">Learning amid overwhelming complexity</h2>
<p>Containers, Kubernetes, AKS, Managed Identity. All of this stuff is new to me. A lot of it is rather complex.</p>

<p>How do we tackle learning these systems when they can seem overwhelmingly full of features and settings? Their flexibility and power are amazing, but that comes at a cost of cognitive overload when you are trying to dip your toe into the water and test things out for the first time.</p>

<p>I think <a href="https://www.hanselman.com/">Scott Hanselman</a> did a fantastic job discussing this topic in his recent video on <a href="https://www.youtube.com/watch?v=mJ_-VoEB6zM">understanding the cloud</a>.</p>

<p>Scott shows a rather hilariously dense poster showing all of the various Azure services and components and, well, overwhelming is a good way to describe it.</p>

<p><img src="/images/2020-11-29/azure1.jpg" style="width:80%; height:80%;float:top" /></p>

<p>This is, incidentally, one of the fundamental tasks of a software developer: managing complexity. What techniques can we employ to keep our sanity?</p>

<p>Scott has some great advice (watch the video!) along the lines of going back to basics and starting simple, building up a deeper understanding from the ground up, piece by piece.</p>

<p>A book that was influential to me when I was first starting my career was <a href="https://www.amazon.com/Most-Complex-Machine-Computers-Computing/dp/1568810547">The Most Complex Machine</a>. A central theme of that book is complexity and how to deal with it. For someone self-taught who found computers to be unfathomably complicated beasts, it was enlightening to see how the ‘bottoms up’ approach to solving complex problems can be such a critical tool to understanding. The ‘black box’ technique, where we abstract away the internal complexity of an individual component or system by treating it as an opaque box that takes inputs and produces outputs, is extremely useful here. Sometimes we need to crack open the box and see what’s inside, but often we can stay at a level of abstraction where we don’t need to know exactly how it works; we just need to understand how it behaves. Solving complex problems is a matter of starting with simpler pieces and continually going through a process of composition, working upward to build something of greater complexity step-by-step.</p>

<p>The bottom line is that we can avoid being overwhelmed by dividing our overall problem into smaller problems that we can understand and solve individually. The final, more complex working result emerges from composing these solutions together. This is an inherently iterative process: a complete solution does not spring into existence in one fell swoop, but is instead built piece-by-piece, one small solution at a time.</p>

<h2 id="the-primer-i-wished-i-had-found-on-aks-and-managed-identity">The primer I wished I had found on AKS and Managed Identity</h2>
<p>Back to our task: getting code running in AKS to talk successfully to an Azure Key Vault, using Managed Identity.</p>

<p>What follows is going to be the step-by-step walkthrough that I wished I had found when I was trying to get this scenario working.</p>

<p>There are quite a few steps, but we are going to work through them starting from the beginning and build up to a working solution one step at a time.</p>

<h3 id="the-need-for-managed-identity">The need for Managed Identity</h3>
<p>First we might ask: why Managed Identity?</p>

<p>A critical scenario for almost all cloud services is that we need a way of dealing with things that are intended to be kept secret. Database passwords. Certificates used for things like <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS</a>. Credentials to talk to 3rd-party services.</p>

<p>Azure provides a great tool to help you keep such secrets hidden and secure: Key Vaults. However, there is an obvious chicken-and-an-egg problem: if you securely store your various secrets in Key Vault, how do you authenticate your service to prove you have permissions to access those secrets? You need…a secret! In order to generate an authentication credential (typically, an access token) you need a password or a private key or something that is itself a secret. Where do you store that?</p>

<p>One common solution to this problem is to have a certificate installed on every virtual machine where your code is going to run, with the private key for that certificate installed as part of a secure, one-time provisioning process for the VM. The certificate can then be set up to be used as a credential for your application that the authentication server will accept in order to grant an access token. Yes, we still have to install the certificate initially on the VM, but it’s a one-time step and after that we can re-use that VM since it will have the necessary credentials to authenticate. If we wave our hands and say that we have a secure way of doing this provisioning, we’ve solved the problem.</p>

<p>Sort of.</p>

<h4 id="the-peril-of-managing-secrets">The peril of managing secrets</h4>
<p>It’s 7:30 AM and I crack an eye open and glance at my phone. My eyes widen.</p>

<p>Eight missed calls from our team in China, who handles operations while our team in the USA sleeps.
Three missed calls from my boss.
A missed call from my skip-level manager.
An instant message conversation where some of our engineers are clearly desparately trying to get ahold of me.</p>

<p>This is not going to be a good morning.</p>

<p>What happened? Our secure credential that our VMs use, which we provisioned a few years ago, just…expired.</p>

<p>One of our services is down, there is a live site incident and because I happened to be the one who provisioned the credential originally, everyone is hoping I can somehow…‘unexpire’ the credential and get things working again. That’s…not how it works.</p>

<p>Certificates and other credentials expiring or being revoked or otherwise becoming invalid is not that uncommon and has to be right up there with somone pushing a bad DNS change with causing cloud service outages.</p>

<p>Managed Identity solves this problem.</p>

<p>What Manged Identity in Azure does is provide a way to associate such an identity with a particular service, yet in a way such that Azure manages the secret (essentially, the password) for that identity. You still have a <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals">Service Principal</a> that represents your application, but instead of managing any secrets associated with that principal, Azure does it for you. Managed Identity in essence acts as a wrapper around the Service Principal concept.</p>

<p>In this way, you never need to know the secret for the application and do not have to worry about managing it.</p>

<p>In fact, you never have access to the secret so the possibility of doing something insecure and accidentally leaking it is not even a scenario.</p>

<p>Azure takes care of creating the secret and rotating it periodically. It will never expire underneath you or get revoked or anything else that could suddenly cause your service to no longer be able to prove its identity.</p>

<p>It’s a really great feature…if you can figure out how to set it up.</p>

<h2 id="setting-up-managed-identity-in-aks-in-30-simple-steps">Setting up Managed Identity in AKS in 30 simple steps</h2>
<p>Ok, thirty is kind of a ballpark figure, but there are quite a few steps and they are all individually fairly simple.</p>

<p>We are going to start from scratch. I am going to assume that you have an Azure subscription to use and that it is currently empty.</p>

<h3 id="prerequisites">Prerequisites</h3>
<p>We will use the following technologies, so install these in order to follow along:</p>

<p><a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI</a></p>

<p><a href="https://www.docker.com/products/docker-desktop">Docker</a></p>

<p><a href="https://kubernetes.io/">Kubernetes</a> <em>Note: a separate install is not necessarily required, but you will need to enable Kubernetes in Docker Desktop.</em></p>

<p><a href="https://helm.sh/">Helm</a></p>

<p><a href="https://docs.microsoft.com/en-us/powershell/">PowerShell</a></p>

<p><a href="https://dotnet.microsoft.com/download/dotnet/5.0">.NET 5.0 (SDK and runtime)</a></p>

<p><a href="https://k9scli.io/">k9s</a></p>

<p>You will want a code editor as well. I use both Visual Studio 2019 and Visual Studio Code (depending on what I’m doing), but neither are required to go through the rest of this guide.</p>

<p><a href="https://visualstudio.microsoft.com/vs/">Visual Studio 2019</a></p>

<p><a href="https://code.visualstudio.com/">Visual Studio Code</a></p>

<p>As noted, you will also need an Azure subscription and access to the <a href="https://portal.azure.com">Azure Portal</a>.</p>

<h3 id="set-the-azure-cli-subscription-context">Set the Azure CLI subscription context</h3>
<p>Fire up PowerShell in your <a href="https://github.com/Microsoft/Terminal">favorite terminal</a> and let’s get started.</p>

<p>Start by identifying your Subscription ID for the Azure subscription you would like to use. You can identify this by doing the following:</p>

<ol>
  <li>Run <code class="language-plaintext highlighter-rouge">az login</code> to log in to Azure.</li>
  <li>Execute <code class="language-plaintext highlighter-rouge">az account list | ConvertFrom-Json</code> to access your subscriptions.</li>
  <li>Take the GUID from the ‘id’ field of the resulting output for the subscripton you would like to use.</li>
</ol>

<pre style="background: #002451; color: #ffffff; font-family: 'Cascadia Mono'">
cloudName        : AzureCloud
homeTenantId     : 9015d140-0c27-4bd7-b950-cd2ed6d9c3f2
<span style="color:yellow">id               : bd947c6a-ac3f-44f1-a679-c6d6568a80c1</span>
isDefault        : True
managedByTenants : {}
name             : Visual Studio Enterprise Subscription
state            : Enabled
tenantId         : 9015d140-0c27-4bd7-b950-cd2ed6d9c3f2
user             : @{name=user@example.com; type=user}
</pre>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">SUBSCRIPTION_ID</span><span class="o">=</span><span class="s2">"&lt;Your Subscription Id&gt;"</span></code></pre></figure>

<p>We will now set our Azure CLI context to use that subscription.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">az</span><span class="w"> </span><span class="nx">account</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nt">--subscription</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">SUBSCRIPTION_ID</span></code></pre></figure>

<h3 id="create-a-resource-group">Create a resource group</h3>
<p>We will create a resource group for our standard Azure resources, such as Key Vault.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="o">=</span><span class="s2">"aks-demo"</span><span class="w">
</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_LOCATION</span><span class="o">=</span><span class="s2">"westus2"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--location</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_LOCATION</span></code></pre></figure>

<h3 id="create-a-key-vault-and-add-a-secret-to-it">Create a Key Vault and add a secret to it</h3>
<p>Our scenario is going to attempt to access secrets stored in Key Vault, so let’s create one and add our secret data to it.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">KEYVAULT_NAME</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="si">)</span><span class="s2">-vault"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">keyvault</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">KEYVAULT_NAME</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">keyvault</span><span class="w"> </span><span class="nx">secret</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nt">--vault-name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">KEYVAULT_NAME</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nx">AksDemoSecret</span><span class="w"> </span><span class="nt">--value</span><span class="w"> </span><span class="s2">"Sensitive data!"</span></code></pre></figure>

<h3 id="create-an-application-to-read-the-keyvault-secret">Create an application to read the KeyVault secret</h3>
<p>At this point we have a resource group containing a Key Vault and that Key Vault contains a secret we want out application to be able to access.</p>

<p>The goal is to be able to access that secret when our code is running in Kubernetes, in an AKS cluster, without us having to know any secrets up front.</p>

<p>The next step will be to write our application code. This will be a simple .NET 5 application in C# that can access that resource.</p>

<p>Perform the following steps to create a new background worker service project and add the necessary NuGet package references we will need.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">dotnet</span><span class="w"> </span><span class="nx">new</span><span class="w"> </span><span class="nx">worker</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nx">SecretFetcher</span><span class="w">
</span><span class="n">cd</span><span class="w"> </span><span class="o">.</span><span class="nx">/SecretFetcher</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.Extensions.Configuration.AzureKeyVault</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.Azure.Services.AppAuthentication</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.Azure.KeyVault</span><span class="w">
</span><span class="n">dotnet</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">package</span><span class="w"> </span><span class="nx">Microsoft.IdentityModel.Clients.ActiveDirectory</span></code></pre></figure>

<p>Add the name of the key vault we created as a configuration setting, by editing the appsettings.json file and adding an entry for ‘KeyVaultName’:</p>

<p><strong><em>appsettings.json</em></strong></p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
  </span><span class="nl">"Logging"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"LogLevel"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Default"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Warning"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Microsoft.Hosting.Lifetime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Information"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"KeyVaultName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aks-demo-vault"</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>Replace the contents of Program.cs with the following:</p>

<p><strong><em>Program.cs</em></strong></p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">namespace</span> <span class="nn">SecretFetcher</span>
<span class="p">{</span>
    <span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Azure.KeyVault</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Azure.Services.AppAuthentication</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.Configuration</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.Configuration.AzureKeyVault</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">try</span>
            <span class="p">{</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"SecretFetcher started at </span><span class="p">{</span><span class="n">DateTime</span><span class="p">.</span><span class="n">UtcNow</span><span class="p">}</span><span class="s"> UTC!"</span><span class="p">);</span>
                <span class="nf">CreateHostBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">).</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>
            <span class="p">}</span>
            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
                <span class="k">throw</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">IHostBuilder</span> <span class="nf">CreateHostBuilder</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">=&gt;</span>
            <span class="n">Host</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ConfigureServices</span><span class="p">((</span><span class="n">hostContext</span><span class="p">,</span> <span class="n">services</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="p">{</span>
                    <span class="n">services</span><span class="p">.</span><span class="n">AddHostedService</span><span class="p">&lt;</span><span class="n">Worker</span><span class="p">&gt;();</span>
                <span class="p">}).</span><span class="nf">ConfigureAppConfiguration</span><span class="p">((</span><span class="n">context</span><span class="p">,</span> <span class="n">config</span><span class="p">)</span> <span class="p">=&gt;</span>
                <span class="p">{</span>
                    <span class="kt">var</span> <span class="n">configSettings</span> <span class="p">=</span> <span class="n">config</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

                    <span class="n">KeyVaultClient</span> <span class="n">kvc</span><span class="p">;</span>
                    <span class="kt">string</span> <span class="n">token</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">GetEnvironmentVariable</span><span class="p">(</span><span class="s">"ACCESS_TOKEN"</span><span class="p">);</span>

                    <span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrWhiteSpace</span><span class="p">(</span><span class="n">token</span><span class="p">))</span>
                    <span class="p">{</span>
                        <span class="c1">// Local development scenario, use an explicit access token from 'az login' instead of using</span>
                        <span class="c1">// managed identity.</span>
                        <span class="n">kvc</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">KeyVaultClient</span><span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="kt">string</span> <span class="n">_</span><span class="p">,</span> <span class="kt">string</span> <span class="n">_</span><span class="p">,</span> <span class="kt">string</span> <span class="n">_</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">FromResult</span><span class="p">(</span><span class="n">token</span><span class="p">));</span>
                    <span class="p">}</span>
                    <span class="k">else</span>
                    <span class="p">{</span>
                        <span class="c1">// Use managed identity.</span>
                        <span class="kt">var</span> <span class="n">azureServiceTokenProvider</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AzureServiceTokenProvider</span><span class="p">();</span>
                        <span class="n">kvc</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">KeyVaultClient</span><span class="p">(</span>
                            <span class="k">new</span> <span class="n">KeyVaultClient</span><span class="p">.</span><span class="nf">AuthenticationCallback</span><span class="p">(</span><span class="n">azureServiceTokenProvider</span><span class="p">.</span><span class="n">KeyVaultTokenCallback</span><span class="p">));</span>
                    <span class="p">}</span>

                    <span class="n">config</span><span class="p">.</span><span class="nf">AddAzureKeyVault</span><span class="p">(</span>
                        <span class="s">$"https://</span><span class="p">{</span><span class="n">configSettings</span><span class="p">[</span><span class="s">"KeyVaultName"</span><span class="p">]}</span><span class="s">.vault.azure.net/"</span><span class="p">,</span>
                        <span class="n">kvc</span><span class="p">,</span>
                        <span class="k">new</span> <span class="nf">DefaultKeyVaultSecretManager</span><span class="p">());</span>
                <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Note that the code is written to allow either passing an explicit access token in as an environment variable, or relying on managed identity to provide a token automatically via the AzureServiceTokenProvider. The former is to allow running this code in a local docker container, where managed identity is not available since the code is not actually running in Azure.</p>

<p>Replace the contents of Worker.cs with the following:</p>

<p><strong><em>Worker.cs</em></strong></p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">namespace</span> <span class="nn">SecretFetcher</span>
<span class="p">{</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.Configuration</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">System.Threading</span><span class="p">;</span>
    <span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">Worker</span> <span class="p">:</span> <span class="n">BackgroundService</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Worker</span><span class="p">&gt;</span> <span class="n">_logger</span><span class="p">;</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IConfiguration</span> <span class="n">_configuration</span><span class="p">;</span>

        <span class="k">public</span> <span class="nf">Worker</span><span class="p">(</span>
            <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Worker</span><span class="p">&gt;</span> <span class="n">logger</span><span class="p">,</span>
            <span class="n">IConfiguration</span> <span class="n">configuration</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
            <span class="n">_configuration</span> <span class="p">=</span> <span class="n">configuration</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">ExecuteAsync</span><span class="p">(</span><span class="n">CancellationToken</span> <span class="n">stoppingToken</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">while</span> <span class="p">(!</span><span class="n">stoppingToken</span><span class="p">.</span><span class="n">IsCancellationRequested</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">"Worker running at: {time}"</span><span class="p">,</span> <span class="n">DateTimeOffset</span><span class="p">.</span><span class="n">Now</span><span class="p">);</span>
                <span class="kt">var</span> <span class="n">secret</span> <span class="p">=</span> <span class="n">_configuration</span><span class="p">[</span><span class="s">"AksDemoSecret"</span><span class="p">];</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Fetched the following value for 'AksDemoSecret': '</span><span class="p">{</span><span class="n">secret</span><span class="p">}</span><span class="s">'"</span><span class="p">);</span>
                <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">10</span><span class="p">),</span> <span class="n">stoppingToken</span><span class="p">);</span>
                <span class="p">((</span><span class="n">IConfigurationRoot</span><span class="p">)</span><span class="n">_configuration</span><span class="p">).</span><span class="nf">Reload</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Note that the KeyVault access is abstracted away in the following line:</p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="kt">var</span> <span class="n">secret</span> <span class="p">=</span> <span class="n">_configuration</span><span class="p">[</span><span class="s">"AksDemoSecret"</span><span class="p">];</span></code></pre></figure>

<p>Here we are just looking for the value to come from configuration; because the KeyVault middleware we hooked up in Program.cs is implemented as a configuration provider, it is transparent and seamless to our code that the value is actually returned from KeyVault.</p>

<p>The underlying configuration is refreshed every 10 seconds, so if the secret changes we should be able to access the new value.</p>

<p>We are going to run this code in a Linux container, so build and publish the project for Linux:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">dotnet</span><span class="w"> </span><span class="nx">publish</span><span class="w"> </span><span class="nt">-r</span><span class="w"> </span><span class="nx">linux-x64</span></code></pre></figure>

<p>Add a Dockerfile in the same folder as the csproj:</p>

<p><strong><em>Dockerfile</em></strong></p>

<figure class="highlight"><pre><code class="language-docker" data-lang="docker"><span class="k">FROM</span><span class="s"> mcr.microsoft.com/dotnet/runtime:5.0</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">CMD</span><span class="s"> ["./bin/Debug/net5.0/linux-x64/SecretFetcher"]</span></code></pre></figure>

<h3 id="build-and-run-the-docker-image">Build and run the docker image</h3>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">docker</span><span class="w"> </span><span class="nx">build</span><span class="w"> </span><span class="nt">--tag</span><span class="w"> </span><span class="nx">secretfetcher:latest</span><span class="w"> </span><span class="o">.</span></code></pre></figure>

<p>We are going to first run it locally and verify it works, so get an access token we can pass in as an environment variable (again, since managed identity only works when actually running in Azure.)</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">ACCESS_TOKEN</span><span class="o">=</span><span class="p">(</span><span class="n">az</span><span class="w"> </span><span class="nx">account</span><span class="w"> </span><span class="nx">get-access-token</span><span class="w"> </span><span class="nt">--resource</span><span class="o">=</span><span class="n">https://vault.azure.net</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ConvertFrom-Json</span><span class="p">)</span><span class="o">.</span><span class="nf">accessToken</span></code></pre></figure>

<p>Now we can actually start the container and pass in the access token:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">docker</span><span class="w"> </span><span class="nx">run</span><span class="w"> </span><span class="nt">-e</span><span class="w"> </span><span class="nx">ACCESS_TOKEN</span><span class="o">=</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">ACCESS_TOKEN</span><span class="w"> </span><span class="nt">-it</span><span class="w"> </span><span class="n">secretfetcher:latest</span></code></pre></figure>

<p>This should result in some output like the following:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">SecretFetcher started at 11/28/2020 23:50:52 UTC!
info: SecretFetcher.Worker[0]
      Worker running at: 11/28/2020 23:50:53 +00:00
info: SecretFetcher.Worker[0]
      Fetched the following value for 'AksDemoSecret': 'Sensitive data!'
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.</code></pre></figure>

<p>Press ctrl+c to terminate the app running in our local container.</p>

<p>We have successfully set up our Azure resources and written a simple program that retrieves a secret from KeyVault when run in a local docker container. Now we will move on to getting it up and running in Azure.</p>

<h3 id="add-support-for-managed-identity">Add support for managed identity</h3>
<p>We were able to run the application in a local docker image and successfully access KeyVault, because we passed in an access token generated for our current user account, which we authenticated as via <code class="language-plaintext highlighter-rouge">az login</code> at the start of this demo.</p>

<p>When we actually run in Azure, we will use managed identity instead to acquire an access token based on our containerized service being bound to a specific service principal, using a managed identity.</p>

<p>First we create a managed identity:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="o">=</span><span class="s2">"aks-demo-identity"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">identity</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="w">
</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_CLIENT_ID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">az</span><span class="w"> </span><span class="nx">identity</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nt">--query</span><span class="w"> </span><span class="nx">clientId</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="w"> </span><span class="nt">-otsv</span><span class="w">
</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_RESOURCE_ID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">az</span><span class="w"> </span><span class="nx">identity</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nt">--query</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="w"> </span><span class="nt">-otsv</span><span class="w">
</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_PRINCIPAL_ID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">az</span><span class="w"> </span><span class="nx">identity</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nt">--query</span><span class="w"> </span><span class="nx">principalId</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="w"> </span><span class="nt">-otsv</span></code></pre></figure>

<p>Note that we extracted three identifiers for this identity: the client id, the resource id and the principal id. All three will be needed later on, so we saved them to environment variables for future use.</p>

<p>For Key Vault we will give explicit permissions to the managed identity principal we created.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">az</span><span class="w"> </span><span class="nx">keyvault</span><span class="w"> </span><span class="nx">set-policy</span><span class="w"> </span><span class="nt">--secret-permissions</span><span class="w"> </span><span class="nx">get</span><span class="w"> </span><span class="nx">list</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">KEYVAULT_NAME</span><span class="w"> </span><span class="nt">--object-id</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_PRINCIPAL_ID</span></code></pre></figure>

<p>Note that both the <code class="language-plaintext highlighter-rouge">get</code> and <code class="language-plaintext highlighter-rouge">list</code> permissions are needed for our application to successfully retrieve the secret from Key Vault.</p>

<p>At this point we have created the managed identity that we want our application to use to access our Key Vault. We have granted that managed identity <code class="language-plaintext highlighter-rouge">get</code> and <code class="language-plaintext highlighter-rouge">list</code> permissions on secrets in that vault. Now we will need to find a way to apply that managed identity to our application when it is running in Azure.</p>

<h3 id="create-a-container-registry">Create a container registry</h3>
<p>Since our goal is to run a containerized application in Azure, we will create an Azure Container Registry to push our container images to.</p>

<p>Container registry names need to be globally unique, so for the purposes of this demo we are using a little trick and appending five random characters to the registry name to avoid collisions.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span><span class="o">=</span><span class="s2">"demoacr</span><span class="si">$(</span><span class="p">(</span><span class="n">New-Guid</span><span class="p">)</span><span class="o">.</span><span class="nf">Guid</span><span class="o">.</span><span class="nf">Substring</span><span class="p">(</span><span class="nx">0</span><span class="p">,</span><span class="w"> </span><span class="nx">5</span><span class="p">)</span><span class="si">)</span><span class="s2">"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">acr</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span><span class="w"> </span><span class="nt">--sku</span><span class="w"> </span><span class="nx">Basic</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">acr</span><span class="w"> </span><span class="nx">login</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span></code></pre></figure>

<h3 id="push-our-image-to-the-container-registry">Push our image to the container registry</h3>
<p>We need to push our local image to the container registry we created:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">docker</span><span class="w"> </span><span class="nx">build</span><span class="w"> </span><span class="nt">--tag</span><span class="w"> </span><span class="nx">secretfetcher:latest</span><span class="w"> </span><span class="o">.</span><span class="w">
</span><span class="n">docker</span><span class="w"> </span><span class="nx">tag</span><span class="w"> </span><span class="nx">secretfetcher:latest</span><span class="w"> </span><span class="s2">"</span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span><span class="si">)</span><span class="s2">.azurecr.io/secretfetcher:latest"</span><span class="w">
</span><span class="n">docker</span><span class="w"> </span><span class="nx">push</span><span class="w"> </span><span class="s2">"</span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span><span class="si">)</span><span class="s2">.azurecr.io/secretfetcher:latest"</span></code></pre></figure>

<p>Our image is now available to be deployed to Kubernetes.</p>

<h2 id="kubernetes">Kubernetes</h2>
<p>Now we will move on to creating and configuring the Azure Kubernetes Service (AKS) cluster we intend to set up for use with managed identity.</p>

<p>The first step is to create the cluster.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_NAME</span><span class="o">=</span><span class="s2">"aks-demo-cluster"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">aks</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_NAME</span><span class="w"> </span><span class="nt">--enable-managed-identity</span><span class="w"> </span><span class="nt">--attach-acr</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span></code></pre></figure>

<p>Note that we enabled managed identity (<code class="language-plaintext highlighter-rouge">--enable-managed-identity</code>) and attached our container registry (<code class="language-plaintext highlighter-rouge">--attach-acr</code>) as part of this process.</p>

<p>When you create an AKS cluster, the cluster provisioning process will create an additional resource group that the cluster uses for Azure resources that it directly controls. Since one of the main points of Kubernetes is to abstract aways various resources such as virtual machines and networking infrastructure from your applications, it will need to manipulate a variety of Azure resources to achieve that. Those resources will go in this cluster-specific resource group, which will have a name starting with ‘MC_’, followed by the name of the resource group in which you created the AKS cluster, the name of the cluster and the cluster location.</p>

<p>We can compose the name of that group and verify that it exists after running <code class="language-plaintext highlighter-rouge">az aks create</code>.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_RESOURCE_GROUP</span><span class="o">=</span><span class="s2">"MC_</span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="si">)</span><span class="s2">_</span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_NAME</span><span class="si">)</span><span class="s2">_</span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_LOCATION</span><span class="si">)</span><span class="s2">"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_RESOURCE_GROUP</span></code></pre></figure>

<p>Now we will configure kubectl to talk to our cluster:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">az</span><span class="w"> </span><span class="nx">aks</span><span class="w"> </span><span class="nx">get-credentials</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_NAME</span><span class="w"> </span><span class="nt">--overwrite-existing</span></code></pre></figure>

<h3 id="the-two-managed-identities">The two managed identities</h3>
<p>We have two managed identities in play at this point. The first is the one that we explicitly created. We granted that identity permission to read secrets from our Key Vault. That managed identity represents our application and is managed by us.</p>

<p>The second managed identity is the identity of the AKS cluster itself, which was created because we specified the <code class="language-plaintext highlighter-rouge">--enable-managed-identity</code> switch when we ran <code class="language-plaintext highlighter-rouge">az aks create</code>. The managed identity of the cluster can be found by inspecting the <code class="language-plaintext highlighter-rouge">identityProfile.kubeletidentity.clientId</code> property of the cluster. Let’s extract that identifier, as we will need it shortly.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$clientIdPath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"identityProfile.kubeletidentity.clientId"</span><span class="w">
</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_MANAGED_ID</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">az</span><span class="w"> </span><span class="nx">aks</span><span class="w"> </span><span class="nx">show</span><span class="w"> </span><span class="nt">-g</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_NAME</span><span class="w"> </span><span class="nt">--query</span><span class="w"> </span><span class="nv">$clientIdPath</span><span class="w"> </span><span class="nt">-otsv</span></code></pre></figure>

<p>We will not be granting the cluster itself access to our Key Vault or any other of our Azure resources, but we <em>will</em> be granting it specific permissions that allow it to assign our first managed identity (the one with KeyVault access) to applications running on hardware it (the cluster) controls.</p>

<h3 id="role-assignments-for-the-aks-cluster">Role assignments for the AKS cluster</h3>
<p>We need to add the ‘<a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#managed-identity-operator">Managed Identity Operator</a>’ and ‘<a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#virtual-machine-contributor">Virtual Machine Contributor</a>’ role assignments to the AKS cluster’s managed identity.</p>

<p>Specifically:</p>

<ol>
  <li>We assign <code class="language-plaintext highlighter-rouge">Virtual Machine Contributor</code> on the resource group in which the AKS nodes actually run (the <code class="language-plaintext highlighter-rouge">$env:CLUSTER_RESOURCE_GROUP</code>, or the one that starts with MC_…)</li>
  <li>We assign <code class="language-plaintext highlighter-rouge">Managed Identity Operator</code> on the resource group where the managed identity we want our applications to run as was created.</li>
</ol>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$scope</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/subscriptions/</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">SUBSCRIPTION_ID</span><span class="s2">/resourcegroups/</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_RESOURCE_GROUP</span><span class="s2">"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">role</span><span class="w"> </span><span class="nx">assignment</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">--role</span><span class="w"> </span><span class="s2">"Virtual Machine Contributor"</span><span class="w"> </span><span class="nt">--assignee</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_MANAGED_ID</span><span class="w"> </span><span class="nt">--scope</span><span class="w"> </span><span class="nv">$scope</span><span class="w">

</span><span class="nv">$scope</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/subscriptions/</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">SUBSCRIPTION_ID</span><span class="s2">/resourcegroups/</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="s2">"</span><span class="w">
</span><span class="n">az</span><span class="w"> </span><span class="nx">role</span><span class="w"> </span><span class="nx">assignment</span><span class="w"> </span><span class="nx">create</span><span class="w"> </span><span class="nt">--role</span><span class="w"> </span><span class="s2">"Managed Identity Operator"</span><span class="w"> </span><span class="nt">--assignee</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CLUSTER_MANAGED_ID</span><span class="w"> </span><span class="nt">--scope</span><span class="w"> </span><span class="nv">$scope</span></code></pre></figure>

<p><span style="color:darkred;font-weight:bold">The above role assignments must be completed before moving on to the next step, installing AAD Pod Identity into our cluster.</span></p>

<p>Note that the above two steps, assigning these specific roles to the AKS cluster, were the critical piece that I was missing when I tried going through the standard walkthrough on setting up managed identity for an AKS cluster.</p>

<p>At this point the AKS cluster has been granted permissions to apply our managed identity (that can access Key Vault) to applications that run within it, and it has the ability to manage the virtual machines that will be used to run those applications.</p>

<h2 id="aad-pod-identity">AAD Pod Identity</h2>
<p>In order to make managed identity work, we need to install <a href="https://github.com/Azure/aad-pod-identity">AAD Pod Identity</a>. We haven’t used Helm yet, but it makes doing so quite trivial, as you will see below.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">helm</span><span class="w"> </span><span class="nx">repo</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nx">aad-pod-identity</span><span class="w"> </span><span class="nx">https://raw.githubusercontent.com/Azure/aad-pod-identity/master/charts</span><span class="w">
</span><span class="n">helm</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">aad-pod-identity</span><span class="w"> </span><span class="nx">aad-pod-identity/aad-pod-identity</span><span class="w"> </span><span class="nt">--set</span><span class="w"> </span><span class="nx">nmi.allowNetworkPluginKubenet</span><span class="o">=</span><span class="s1">'true'</span></code></pre></figure>

<p>Note that we had to set the <code class="language-plaintext highlighter-rouge">nmi.allowNetworkPluginKubenet</code> value to true. This is because AKS clusters use Kubenet by default and AAD Pod Identity is disabled on Kubenet until you explicitly enable it with this setting. The reason for this is explained on <a href="https://azure.github.io/aad-pod-identity/docs/configure/aad_pod_identity_on_kubenet/">this page</a>.</p>

<p>This was another step that was not clear from the standard walkthrough and was only discoverable by debugging the logs for the AAD Pod Identity containers after they were running in the cluster.</p>

<h3 id="mic-and-nmi-components-of-aad-pod-identity">MIC and NMI components of AAD Pod Identity</h3>
<p>AAD Pod Identity installs two services into your AKS cluster: the <a href="https://azure.github.io/aad-pod-identity/docs/concepts/mic/">Managed Identity Controller (MIC)</a> and something called <a href="https://azure.github.io/aad-pod-identity/docs/concepts/nmi/">Node Managed Identity (NMI)</a>. The MIC is responsible for binding managed identities to virtual machines based on the pods running on those machines. The NMI is responsible for intercepting requests to get an auth token by application code. NMI uses the configured managed identity for the application (which we will set up shortly) as the principal for the access token request.</p>

<h3 id="binding-a-managed-identity-to-an-application-using-aad-pod-identity">Binding a managed identity to an application using AAD Pod Identity</h3>
<p>We need to deploy an identity and identity binding to the cluster. These Kubernetes resources will be used by the MIC to find pods that need to have the specified identity associated with them.</p>

<p>First we define the identity itself, which maps to the managed identity we created earlier and gave access to our Key Vault:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$cmd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"
apiVersion: </span><span class="se">`"</span><span class="s2">aadpodidentity.k8s.io/v1</span><span class="se">`"</span><span class="s2">
kind: AzureIdentity
metadata:
  name: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="si">)</span><span class="s2">
spec:
  type: 0
  resourceID: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_RESOURCE_ID</span><span class="si">)</span><span class="s2">
  clientID: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_CLIENT_ID</span><span class="si">)</span><span class="s2">
"</span><span class="w">

</span><span class="nv">$cmd</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">kubectl</span><span class="w"> </span><span class="nx">apply</span><span class="w"> </span><span class="nt">-f</span><span class="w"> </span><span class="o">-</span></code></pre></figure>

<p>We next create a binding resource, which uses the <code class="language-plaintext highlighter-rouge">selector</code> property to find deployments having a specific label. If a deployment has such a label, it’s essentially saying ‘apply the related managed identity to the VM where my pod is running!’</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$cmd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"
apiVersion: </span><span class="se">`"</span><span class="s2">aadpodidentity.k8s.io/v1</span><span class="se">`"</span><span class="s2">
kind: AzureIdentityBinding
metadata:
  name: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="si">)</span><span class="s2">-binding
spec:
  azureIdentity: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="si">)</span><span class="s2">
  selector: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="si">)</span><span class="s2">
"</span><span class="w">

</span><span class="nv">$cmd</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">kubectl</span><span class="w"> </span><span class="nx">apply</span><span class="w"> </span><span class="nt">-f</span><span class="w"> </span><span class="o">-</span></code></pre></figure>

<p>When we deploy our application, we need to add the special label that the MIC will use to apply the binding we just created. That label is called <code class="language-plaintext highlighter-rouge">aadpodidbinding</code> and is added to the labels in the metadata section of the template in our deployment YAML.</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml">  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">aadpodidbinding</span><span class="pi">:</span> <span class="s">$($env:IDENTITY_NAME)</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">secretfetcher</span></code></pre></figure>

<p>The value of the <code class="language-plaintext highlighter-rouge">aadpodidbinding</code> must match that of the <code class="language-plaintext highlighter-rouge">selector</code> in the binding we created previously. Note that this value does not have to be the name of the managed identity (although that’s as good a value to use as any), it can be any string value. The important part is that they match.</p>

<p><img src="/images/2020-11-29/diagram1.png" style="width:50; height:50%;float:top" /></p>

<p>Let’s create the appropriate YAML for our test application, including the critical <code class="language-plaintext highlighter-rouge">aadpodidbinding</code> label, and deploy the application into our AKS cluster.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="nv">$cmd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secretfetcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: secretfetcher
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  minReadySeconds: 5
  template:
    metadata:
      labels:
        aadpodidbinding: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">IDENTITY_NAME</span><span class="si">)</span><span class="s2">
        app: secretfetcher
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - name: secretfetcher01
        image:  </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">CONTAINER_REGISTRY</span><span class="si">)</span><span class="s2">.azurecr.io/secretfetcher:latest
        env:
          - name: KEYVAULT_NAME
            value: </span><span class="si">$(</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">KEYVAULT_NAME</span><span class="si">)</span><span class="s2">
        resources:
          requests:
          limits:
            memory: 256Mi
        securityContext:
          capabilities:
            drop:
            - NET_RAW
"</span><span class="w">

</span><span class="nv">$cmd</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">kubectl</span><span class="w"> </span><span class="nx">apply</span><span class="w"> </span><span class="nt">-f</span><span class="w"> </span><span class="o">-</span></code></pre></figure>

<p>At this point we can check which pods are running. We will see the pods created by AAD Pod Identity for both the MIC and NMI services, as well as our own pod for the SecretFetcher application.</p>

<p>Here is one way to get the status of the pods running in our cluster:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">kubectl</span><span class="w"> </span><span class="nx">get</span><span class="w"> </span><span class="nx">pods</span><span class="w">

</span><span class="n">NAME</span><span class="w">                                    </span><span class="nx">READY</span><span class="w">   </span><span class="nx">STATUS</span><span class="w">    </span><span class="nx">RESTARTS</span><span class="w">   </span><span class="nx">AGE</span><span class="w">
</span><span class="n">aad-pod-identity-mic-5cd47f7cd7-b2wdd</span><span class="w">   </span><span class="nx">1/1</span><span class="w">     </span><span class="nx">Running</span><span class="w">   </span><span class="nx">0</span><span class="w">          </span><span class="nx">34s</span><span class="w">
</span><span class="n">aad-pod-identity-mic-5cd47f7cd7-n52kh</span><span class="w">   </span><span class="nx">1/1</span><span class="w">     </span><span class="nx">Running</span><span class="w">   </span><span class="nx">0</span><span class="w">          </span><span class="nx">34s</span><span class="w">
</span><span class="n">aad-pod-identity-nmi-7sv85</span><span class="w">              </span><span class="nx">1/1</span><span class="w">     </span><span class="nx">Running</span><span class="w">   </span><span class="nx">0</span><span class="w">          </span><span class="nx">34s</span><span class="w">
</span><span class="n">aad-pod-identity-nmi-kx5c6</span><span class="w">              </span><span class="nx">1/1</span><span class="w">     </span><span class="nx">Running</span><span class="w">   </span><span class="nx">0</span><span class="w">          </span><span class="nx">34s</span><span class="w">
</span><span class="n">aad-pod-identity-nmi-wf59l</span><span class="w">              </span><span class="nx">1/1</span><span class="w">     </span><span class="nx">Running</span><span class="w">   </span><span class="nx">0</span><span class="w">          </span><span class="nx">34s</span><span class="w">
</span><span class="n">secretfetcher-67dc7dbd59-fphcb</span><span class="w">          </span><span class="nx">1/1</span><span class="w">     </span><span class="nx">Running</span><span class="w">   </span><span class="nx">2</span><span class="w">          </span><span class="nx">43s</span></code></pre></figure>

<p>While we can get this information from kubectl easily enough, I highly recommend using the very useful <a href="https://k9scli.io/">k9s</a> tool. I run this tool in a separate pane in my terminal so that all changes I make to my cluster can be seen immediately. The tool allows drilling in to individual containers to get logs, spawning a remote shell, killing running pods and so on - all in a simple to use, terminal-based interface.</p>

<p><img src="/images/2020-11-29/k9s2.png" style="width:100%; height:100%;float:top" /></p>

<p>If everything has been configured properly (and if you followed the steps above carefully, it should be), we can now check our logs and see that we are able to successfully access KeyVault using managed identity:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">kubectl</span><span class="w"> </span><span class="nx">logs</span><span class="w"> </span><span class="nx">secretfetcher-67dc7dbd59-fphcb</span><span class="w">

</span><span class="n">SecretFetcher</span><span class="w"> </span><span class="nx">started</span><span class="w"> </span><span class="nx">at</span><span class="w"> </span><span class="nx">11/29/2020</span><span class="w"> </span><span class="nx">03:03:21</span><span class="w"> </span><span class="nx">UTC</span><span class="o">!</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">SecretFetcher.Worker</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Worker</span><span class="w"> </span><span class="nx">running</span><span class="w"> </span><span class="nx">at:</span><span class="w"> </span><span class="nx">11/29/2020</span><span class="w"> </span><span class="nx">03:04:14</span><span class="w"> </span><span class="o">+</span><span class="nx">00:00</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">SecretFetcher.Worker</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Fetched</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">following</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="nx">for</span><span class="w"> </span><span class="s1">'AksDemoSecret'</span><span class="p">:</span><span class="w"> </span><span class="s1">'Sensitive Data!'</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">Microsoft.Hosting.Lifetime</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Application</span><span class="w"> </span><span class="nx">started.</span><span class="w"> </span><span class="nx">Press</span><span class="w"> </span><span class="nx">Ctrl</span><span class="o">+</span><span class="nx">C</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">shut</span><span class="w"> </span><span class="nx">down.</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">Microsoft.Hosting.Lifetime</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Hosting</span><span class="w"> </span><span class="nx">environment:</span><span class="w"> </span><span class="nx">Production</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">Microsoft.Hosting.Lifetime</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Content</span><span class="w"> </span><span class="nx">root</span><span class="w"> </span><span class="nx">path:</span><span class="w"> </span><span class="nx">/app</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">SecretFetcher.Worker</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Worker</span><span class="w"> </span><span class="nx">running</span><span class="w"> </span><span class="nx">at:</span><span class="w"> </span><span class="nx">11/29/2020</span><span class="w"> </span><span class="nx">03:04:24</span><span class="w"> </span><span class="o">+</span><span class="nx">00:00</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">SecretFetcher.Worker</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Fetched</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">following</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="nx">for</span><span class="w"> </span><span class="s1">'AksDemoSecret'</span><span class="p">:</span><span class="w"> </span><span class="s1">'Sensitive Data!'</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">SecretFetcher.Worker</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Worker</span><span class="w"> </span><span class="nx">running</span><span class="w"> </span><span class="nx">at:</span><span class="w"> </span><span class="nx">11/29/2020</span><span class="w"> </span><span class="nx">03:04:34</span><span class="w"> </span><span class="o">+</span><span class="nx">00:00</span><span class="w">
</span><span class="n">info:</span><span class="w"> </span><span class="nx">SecretFetcher.Worker</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w">
      </span><span class="n">Fetched</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">following</span><span class="w"> </span><span class="nx">value</span><span class="w"> </span><span class="nx">for</span><span class="w"> </span><span class="s1">'AksDemoSecret'</span><span class="p">:</span><span class="w"> </span><span class="s1">'Sensitive Data!'</span></code></pre></figure>

<p>Success! We have started from scratch and properly configured our Azure environment so that we can successfully deploy and run a containerized application in AKS and use Managed Identity to seamlessly connect to our Key Vault.</p>

<p>It might be instructive to see what happens when AAD Pod Identity is not deployed. Let’s stop our existing application:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">kubectl</span><span class="w"> </span><span class="nx">scale</span><span class="w"> </span><span class="nx">deployments/secretfetcher</span><span class="w"> </span><span class="nt">--replicas</span><span class="o">=</span><span class="mi">0</span></code></pre></figure>

<p>…and then uninstall AAD Pod Identity and restart our application:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">helm</span><span class="w"> </span><span class="nx">uninstall</span><span class="w"> </span><span class="nx">aad-pod-identity</span><span class="w">
</span><span class="n">kubectl</span><span class="w"> </span><span class="nx">scale</span><span class="w"> </span><span class="nx">deployments/secretfetcher</span><span class="w"> </span><span class="nt">--replicas</span><span class="o">=</span><span class="mi">1</span></code></pre></figure>

<p>Inspecting the logs for our application should now show that it’s failing miserably.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">kubectl</span><span class="w"> </span><span class="nx">logs</span><span class="w"> </span><span class="nt">-l</span><span class="w"> </span><span class="s2">"app=secretfetcher"</span><span class="w"> </span><span class="nt">--tail</span><span class="w"> </span><span class="nx">100</span><span class="w">

</span><span class="n">SecretFetcher</span><span class="w"> </span><span class="nx">started</span><span class="w"> </span><span class="nx">at</span><span class="w"> </span><span class="nx">11/29/2020</span><span class="w"> </span><span class="nx">03:23:18</span><span class="w"> </span><span class="nx">UTC</span><span class="o">!</span><span class="w">
</span><span class="n">Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException:</span><span class="w"> </span><span class="nx">Parameters:</span><span class="w"> </span><span class="nx">Connection</span><span class="w"> </span><span class="nx">String:</span><span class="w"> </span><span class="p">[</span><span class="n">No</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="n">specified</span><span class="p">],</span><span class="w"> </span><span class="nx">Resource:</span><span class="w"> </span><span class="nx">https://vault.azure.net</span><span class="p">,</span><span class="w"> </span><span class="nx">Authority:</span><span class="w"> </span><span class="nx">https://login.windows.net/f57bf9a8-a074-4a10-a754-1b05e4f6e8d9.</span><span class="w"> </span><span class="nx">Exception</span><span class="w"> </span><span class="nx">Message:</span><span class="w"> </span><span class="nx">Tried</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">following</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="nx">methods</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">get</span><span class="w"> </span><span class="nx">an</span><span class="w"> </span><span class="nx">access</span><span class="w"> </span><span class="nx">token</span><span class="p">,</span><span class="w"> </span><span class="nx">but</span><span class="w"> </span><span class="nx">none</span><span class="w"> </span><span class="nx">of</span><span class="w"> </span><span class="nx">them</span><span class="w"> </span><span class="nx">worked.</span><span class="w">
</span><span class="n">Parameters:</span><span class="w"> </span><span class="nx">Connection</span><span class="w"> </span><span class="nx">String:</span><span class="w"> </span><span class="p">[</span><span class="n">No</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="n">specified</span><span class="p">],</span><span class="w"> </span><span class="nx">Resource:</span><span class="w"> </span><span class="nx">https://vault.azure.net</span><span class="p">,</span><span class="w"> </span><span class="nx">Authority:</span><span class="w"> </span><span class="nx">https://login.windows.net/f57bf9a8-a074-4a10-a754-1b05e4f6e8d9.</span><span class="w"> </span><span class="nx">Exception</span><span class="w"> </span><span class="nx">Message:</span><span class="w"> </span><span class="nx">Tried</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">get</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">using</span><span class="w"> </span><span class="nx">Managed</span><span class="w"> </span><span class="nx">Service</span><span class="w"> </span><span class="nx">Identity.</span><span class="w"> </span><span class="nx">Access</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">could</span><span class="w"> </span><span class="nx">not</span><span class="w"> </span><span class="nx">be</span><span class="w"> </span><span class="nx">acquired.</span><span class="w"> </span><span class="nx">Received</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">non-retryable</span><span class="w"> </span><span class="nx">error.</span><span class="w"> </span><span class="nx">MSI</span><span class="w"> </span><span class="nx">ResponseCode:</span><span class="w"> </span><span class="nx">BadRequest</span><span class="p">,</span><span class="w"> </span><span class="nx">Response:</span><span class="w"> </span><span class="p">{</span><span class="s2">"error"</span><span class="p">:</span><span class="s2">"invalid_request"</span><span class="p">,</span><span class="s2">"error_description"</span><span class="p">:</span><span class="s2">"Identity not found"</span><span class="p">}</span><span class="w">
</span><span class="n">Parameters:</span><span class="w"> </span><span class="nx">Connection</span><span class="w"> </span><span class="nx">String:</span><span class="w"> </span><span class="p">[</span><span class="n">No</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="n">specified</span><span class="p">],</span><span class="w"> </span><span class="nx">Resource:</span><span class="w"> </span><span class="nx">https://vault.azure.net</span><span class="p">,</span><span class="w"> </span><span class="nx">Authority:</span><span class="w"> </span><span class="nx">https://login.windows.net/f57bf9a8-a074-4a10-a754-1b05e4f6e8d9.</span><span class="w"> </span><span class="nx">Exception</span><span class="w"> </span><span class="nx">Message:</span><span class="w"> </span><span class="nx">Tried</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">get</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">using</span><span class="w"> </span><span class="nx">Visual</span><span class="w"> </span><span class="nx">Studio.</span><span class="w"> </span><span class="nx">Access</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">could</span><span class="w"> </span><span class="nx">not</span><span class="w"> </span><span class="nx">be</span><span class="w"> </span><span class="nx">acquired.</span><span class="w"> </span><span class="nx">Environment</span><span class="w"> </span><span class="nx">variable</span><span class="w"> </span><span class="nx">LOCALAPPDATA</span><span class="w"> </span><span class="nx">not</span><span class="w"> </span><span class="nx">set.</span><span class="w">
</span><span class="n">Parameters:</span><span class="w"> </span><span class="nx">Connection</span><span class="w"> </span><span class="nx">String:</span><span class="w"> </span><span class="p">[</span><span class="n">No</span><span class="w"> </span><span class="n">connection</span><span class="w"> </span><span class="n">string</span><span class="w"> </span><span class="n">specified</span><span class="p">],</span><span class="w"> </span><span class="nx">Resource:</span><span class="w"> </span><span class="nx">https://vault.azure.net</span><span class="p">,</span><span class="w"> </span><span class="nx">Authority:</span><span class="w"> </span><span class="nx">https://login.windows.net/f57bf9a8-a074-4a10-a754-1b05e4f6e8d9.</span><span class="w"> </span><span class="nx">Exception</span><span class="w"> </span><span class="nx">Message:</span><span class="w"> </span><span class="nx">Tried</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">get</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">using</span><span class="w"> </span><span class="nx">Azure</span><span class="w"> </span><span class="nx">CLI.</span><span class="w"> </span><span class="nx">Access</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">could</span><span class="w"> </span><span class="nx">not</span><span class="w"> </span><span class="nx">be</span><span class="w"> </span><span class="nx">acquired.</span></code></pre></figure>

<p>Re-installing AAD Pod Identity should immediately fix the problem. Note that we don’t need to re-deploy the identity and identity binding resources, as those were never removed from the cluster.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">helm</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">aad-pod-identity</span><span class="w"> </span><span class="nx">aad-pod-identity/aad-pod-identity</span><span class="w"> </span><span class="nt">--set</span><span class="w"> </span><span class="nx">nmi.allowNetworkPluginKubenet</span><span class="o">=</span><span class="s1">'true'</span></code></pre></figure>

<p>If you are running k9s, you should see the application flip from the red ‘failed’ state to successfully running fairly quickly after re-installing AAD Pod Identity.</p>

<h2 id="cleaning-up">Cleaning up</h2>
<p>We are all done with this walkthrough, so we can clean up all of the resources we created and get back to a clean state in our subscription. This will ensure we don’t pay for resources that we will not be using going forward.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">az</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">delete</span><span class="w"> </span><span class="nt">--resource-group</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">RESOURCE_GROUP</span><span class="w"> </span><span class="nt">--yes</span></code></pre></figure>

<p>Note that we do not need to explicitly delete the resource group used by the AKS cluster for its own resources; deleting the cluster itself will automatically clean that up.</p>

<p>After deleting our resource group, some resources such as our KeyVault instance may be left in a ‘soft deleted’ state.</p>

<p>These can be cleaned up via:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">az</span><span class="w"> </span><span class="nx">keyvault</span><span class="w"> </span><span class="nx">purge</span><span class="w"> </span><span class="nt">--name</span><span class="w"> </span><span class="nx">env:KEYVAULT_NAME</span></code></pre></figure>

<h2 id="conclusion">Conclusion</h2>
<p>Moving one step at a time from a clean starting state, we built up all of the necessary infrastructure and pieces to achieve our end goal.</p>

<p>In the end, getting this end-to-end scenario working was not really all that difficult. We simply needed to understand all of the pieces in play and how they fit together. In particular, we needed to understand the importance of assigning the <code class="language-plaintext highlighter-rouge">Virtual Machine Contributor</code> and <code class="language-plaintext highlighter-rouge">Managed Identity Operator</code> roles to the identity associated with our AKS cluster <em>before</em> installing AAD Pod Identity. We also needed to create the correct identity binding in the cluster so that the components of AAD Pod Identity can apply the correct managed identity to our application.</p>

<p>I certainly learned quite a bit going through this process. I hope you found it instructive too.</p>]]></content><author><name></name></author><category term="C#," /><category term="Azure," /><category term="Kubernetes," /><category term="KeyVault" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">A C# programmer examines Rust - Part 2</title><link href="/rust,/c%23,/programming/2020/03/15/StartingRustPart2.html" rel="alternate" type="text/html" title="A C# programmer examines Rust - Part 2" /><published>2020-03-15T13:02:00+00:00</published><updated>2020-03-15T13:02:00+00:00</updated><id>/rust,/c%23,/programming/2020/03/15/StartingRustPart2</id><content type="html" xml:base="/rust,/c%23,/programming/2020/03/15/StartingRustPart2.html"><![CDATA[<p><img src="/images/2020-03-15/rust1.png" style="width:25%; height:25%;float:right" /></p>

<p>In <a href="/programming,/rust,/c%23/2020/03/06/StartingRust.html">part one</a> we started an initial exploration of the Rust programming language, using a small programming problem as a starting point. The problem was:</p>

<blockquote>
  <p>Given a string which contains comma-separated values, transform it into a list of Globally Unique Identifiers (GUIDs) if and only if every value is a valid GUID. Ignore whitespace and newlines.</p>
</blockquote>

<p>The following Rust function was given as a candidate solution, which proved to capably solve the problem while retaining execellent runtime performance:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">fn</span> <span class="nf">extract_ids</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="nb">str</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
    <span class="n">input</span>
        <span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span>
        <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="n">s</span><span class="nf">.trim</span><span class="p">())</span>
        <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="nn">Uuid</span><span class="p">::</span><span class="nf">parse_str</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="nf">.ok</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="n">s</span><span class="p">))</span>
        <span class="nf">.collect</span><span class="p">()</span>
<span class="p">}</span></code></pre></figure>

<p>This is not necessarily the most optimal way to solve this problem, but it is succinct and correct and good enough for our initial purposes.</p>

<p>Is it obvious, though, how it works?</p>

<p>Let’s unpack it.</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">fn</span> <span class="nf">extract_ids</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="o">...</span></code></pre></figure>

<p>We have a function that takes as input an <code class="language-plaintext highlighter-rouge">&amp;str</code>, which is Rust’s way of saying a ‘reference to a <a href="https://doc.rust-lang.org/std/primitive.str.html">string slice</a>.’</p>

<h3 id="references-ownership-and-borrows">References, ownership and borrows</h3>
<p>If you have never programmed Rust, it would be very useful to read up on <a href="https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html">ownership</a>. This is one of the fundamental concepts in Rust and one of the things that makes the language novel and interesting. Coming from a garbage collected language like C#, the strict ownership rules in Rust might seem onerous, and even bizarre. The rewards for following these rules, however, are great: memory safety without performance overhead.</p>

<p>I’m not going to attempt to explain all of the details of Rust’s ownership system in this blog post, but we can summarize the salient points:</p>

<ol>
  <li>Creating a reference to some memory location is known as ‘borrowing’ that memory location.</li>
  <li>References (borrows) of a given location are immutable by default.</li>
  <li>You can have as many immutable borrows of a location in memory as you like.</li>
  <li>You can have exactly one mutable borrow of a memory location at any given time.</li>
  <li>You cannot create a mutable borrow while there are existing immutable borrows.</li>
  <li>Accessing a memory location without borrowing it implies transferring ownership of that location. This is called ‘moving’ ownership.</li>
  <li>There are exceptions for types that can be cheaply copied. This include primitive types such as integers, where accessing the memory location for the type makes a copy of the bits at the original location. C# programmers will be familiar with that as the difference between ‘value types’ and ‘reference types’ in that language.</li>
</ol>

<p>Ok, this sounds like a lot of rules to have to keep track of. Once you grasp the rules, however, (and if you don’t, believe me the compiler will let you know) it is not too hard to get comfortable with the system. The results are well worth it.</p>

<p>So: using the <code class="language-plaintext highlighter-rouge">&amp;</code> symbol to create a reference means taking a borrow of the memory in question. We use <code class="language-plaintext highlighter-rouge">&amp;mut</code> to take a mutable borrow.</p>

<p>The Rust compiler keeps track of all of the memory in use by the program, who owns it, and what borrows of that memory exist. When the owner of a piece of memory goes out of scope (i.e., is no longer used elsewhere in the program), the memory that was owned is freed.</p>

<p>This is how Rust achieves memory safety without a garbage collector: the compiler does all of the work!</p>

<p>(This does mean compile times in Rust can be atrocious, but it’s hard to argue that this inconvenience outweighs the somewhat incredible end result.)</p>

<h3 id="examining-the-input-type">Examining the input type</h3>
<p>We can see that our function takes as input a <code class="language-plaintext highlighter-rouge">&amp;str</code>, which is an immutable reference (borrow) of a string <a href="https://doc.rust-lang.org/std/str/">slice</a>. A string slice is simply a pointer to some UTF8 string and a length, similar to the Span<char> and ReadOnlySpan<char> types in C#.</char></char></p>

<p>As there is no <code class="language-plaintext highlighter-rouge">null</code> reference concept in Rust (more on that in a bit), we avoid needing to write boilerplate code to validate that we were not passed input that can cause us to crash. This helps keep the code succinct while still being correct.</p>

<p>Incidentally, lack of boilerplate is one of those things that drew me to functional programming (F#) originally, and Rust happily continues that experience. C#, on the other hand, is boilerplate city.</p>

<h3 id="examining-the-return-type">Examining the return type</h3>
<p>The return type of our function is <code class="language-plaintext highlighter-rouge">Option&lt;Vec&lt;&amp;str&gt;&gt;</code>. What’s this?</p>

<p>Well, we are using generic type parameters here so really what we have is: a <code class="language-plaintext highlighter-rouge">Vec&lt;T&gt;</code>, where <code class="language-plaintext highlighter-rouge">T</code> is an <code class="language-plaintext highlighter-rouge">&amp;str</code>, and an <code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code>, where <code class="language-plaintext highlighter-rouge">T</code> is…our <code class="language-plaintext highlighter-rouge">Vec&lt;&amp;str&gt;</code>.</p>

<p>Generic type parameters here work the same as in C# code. The type parameter is a placeholder that gets replaced by some concrete type at compile time.</p>

<p><code class="language-plaintext highlighter-rouge">Vec&lt;T&gt;</code> in Rust is analogous to <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> in C#. It’s a list type that grows as needed to accomodate new items being added to it.</p>

<h4 id="falling-in-love-with-the-option-type">Falling in love with the Option type</h4>
<p>So, what’s an <a href="https://doc.rust-lang.org/std/option/"><code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code></a>?</p>

<p>Option types are one solution to <a href="https://www.linkedin.com/pulse/20141126171912-7082046-tony-hoare-invention-of-the-null-reference-a-billion-dollar-mistake/">the billion dollar mistake</a>, the ability to have null references. They are, for me, one of the best ideas from the world of functional programming languages. The Option type provides an explicit way to encapsulate the idea that an operation either produced a value, or it produced nothing. What’s more, programming languages like Rust require your code to account for both possibilites; if you do not, the code will not compile! For seasoned C# programmers this is a concept that takes a bit of getting used to…imagine a world where you never have to check arguments for null, you never have to worry about accessing a null reference,  the null coalescing and null conditional operators don’t exist…</p>

<p>An Option is a form of discriminated union, meaning it can contain exactly one of the possible choices for that union. For Option, we either have Some(T) (meaning, we have produced a value of type T) or None. Those are the two possibilities.</p>

<p>Because the <code class="language-plaintext highlighter-rouge">None</code> value is not something that is going to convert to a value of type <code class="language-plaintext highlighter-rouge">T</code>, the type system in Rust prevents blindly passing in an Option result to something that expects a T.</p>

<p>If you are thinking having to write code to always test every operation for both the Some(T) and None possibilities is going to be tedious, it’s really not! Rust provides some nice syntactic ways to do pattern matching on Option types that make the code straight-forward.</p>

<p>Here is some sample code where we can see Option in action:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">fn</span> <span class="nf">make_a_slice</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"Hello!"</span><span class="p">;</span>

    <span class="c1">// Create a slice, starting at index one and going up to,</span>
    <span class="c1">// but not including, index three.</span>
    <span class="k">let</span> <span class="n">opt</span> <span class="o">=</span> <span class="n">s</span><span class="nf">.get</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">3</span><span class="p">);</span>

    <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">slice</span><span class="p">)</span> <span class="o">=</span> <span class="n">opt</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"{} - {}"</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">slice</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Try to create an invalid slice that is much too large.</span>
    <span class="k">let</span> <span class="n">opt</span> <span class="o">=</span> <span class="n">s</span><span class="nf">.get</span><span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">1000</span><span class="p">);</span>

    <span class="k">match</span> <span class="n">opt</span> <span class="p">{</span>
        <span class="nf">Some</span><span class="p">(</span><span class="n">slice</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"{} - {}"</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">slice</span><span class="p">),</span>
        <span class="nb">None</span> <span class="k">=&gt;</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"Did not produce a slice!"</span><span class="p">),</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>This illustrates two possible ways (<code class="language-plaintext highlighter-rouge">if let</code> and <code class="language-plaintext highlighter-rouge">match</code>) of pattern matching on an Option result and extracting the result, if any.</p>

<h3 id="declarative-coding-fun-with-iterators">Declarative coding fun with iterators</h3>
<p>We have examined the input and return types for our sample string processing function: we will either produce Some(Vec&lt;&amp;str&gt;) for the case where everything passed in was a valid GUID, or we will produce None.</p>

<p>Now let’s look at the actual computation the function is performing:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="n">input</span>
    <span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span>
    <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="n">s</span><span class="nf">.trim</span><span class="p">())</span>
    <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="nn">Uuid</span><span class="p">::</span><span class="nf">parse_str</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="nf">.ok</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="n">s</span><span class="p">))</span>
    <span class="nf">.collect</span><span class="p">()</span></code></pre></figure>

<p>If you have used LINQ in C# this probably looks somewhat familiar; iterators in Rust are similar to IEnumerable<T> in C#, where LINQ provides a rich set of operations that can be applied to the sequence. Both are lazily evaluated and both support chaining operations together to express the logic used to produce a final result.</T></p>

<p>A major difference is that with LINQ you typically pay a performance penalty for this terse style of declarative coding, whereas with Rust you do not!</p>

<p>Let’s compare some significant differences between C# and Rust.</p>

<p>We will start by modifying our example code to simply tokenize our input string and just print the first two tokens after trimming any whitespace.</p>

<p>Here is a Rust version:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">let</span> <span class="n">tokens</span> <span class="o">=</span> <span class="n">input</span>
    <span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span>
    <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="n">s</span><span class="nf">.trim</span><span class="p">())</span>
    <span class="nf">.take</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>

<span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="n">tokens</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>Here is a C# version:</p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="kt">var</span> <span class="n">tokens</span> <span class="p">=</span> <span class="n">input</span>
    <span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="s">","</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="n">s</span><span class="p">.</span><span class="nf">Trim</span><span class="p">())</span>
    <span class="p">.</span><span class="nf">Take</span><span class="p">(</span><span class="m">2</span><span class="p">);</span>

<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">token</span> <span class="k">in</span> <span class="n">tokens</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">token</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>Both pieces of code provide a very natural, ergonomic way to tokenize a delimited string and get the first couple of entries.</p>

<p>There is a reason, however, that every time I see string.Split() called in C# my eyes narrow with suspicion.</p>

<p>Consider our original example scenario from part one of this series where we were loading a lot of text: 10 million GUIDs, along with a bunch of commas and whitespace characters. On disk it’s about 450MB of data. In C# when we call Split(), how many strings did we just allocate? Well, the method returns a string[], that array had to be allocated and filled with the results of splitting, so we just allocated 10 million additional strings. We then allocate an additional ‘trimmed’ string for each item we iterate, since we are calling string.Trim() to remove the whitespace, which allocates yet another string. At least lazy evaluation saves us from allocating 10 million trimmed strings!</p>

<p>A memorable line from a talk I saw some time back was: the garbage collector isn’t your problem, the garbage is your problem!</p>

<p>In reality, C# makes it very, very easy to allocate on the heap like crazy and, while there are ways to avoid this if you are a particularly careful programmer, most developers go with the path of least resistance. There are a <em>lot</em> of string.Split() calls out there in the real world.</p>

<p>You probably saw this next bit coming. How many new strings does the Rust code allocate?</p>

<p>Zero.</p>

<p>Remember that &amp;str is a reference to a string <em>slice</em>, which is just a pointer to the underlying string and a length. With default immutability having our back, is it really necessary to allocate an entirely new string when splitting? What if we just…adjusted where our slice points to? We could point to the first non-comma character and set the length to end just before the next comma. When we trim the string, can’t we just slide the pointer to the first non-whitespace character and set the length to end just before the next whitespace character? In addition, split() being lazy by default (unlike the C# version) means even if we need to stack allocate some slice instances as we are iterating, we don’t need to create millions of them. We are saving hundreds of megabytes of unnecessary memory allocations, yet we wrote essentially the exact same code.</p>

<p>(Caveat: I haven’t looked into how split is actually implemented in Rust, I’m just passing along what I have learned by observing the behavior through some experimentation with a debugger attached.)</p>

<p>Ok, let’s prove that Rust isn’t allocating strings. Recall that our input consists of a bunch of whitespace and GUIDs and commas. Let’s assume our giant 450MB file starts like this:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">    b55af7cc-e4f2-4f8e-8b0e-18582d9feaba,          7ce3cb99-8b43-486c-b9a8-89bb1807651f, </code></pre></figure>

<p>(and goes on for millions of more such entries.)</p>

<p>We read that entire file in as a string and then we do our splitting and trimming and taking.</p>

<p>Let’s look in the debugger what ‘x’ points to on the first iteration.</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">let</span> <span class="n">tokens</span> <span class="o">=</span> <span class="n">input</span>
    <span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span>
    <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="n">s</span><span class="nf">.trim</span><span class="p">())</span>
    <span class="nf">.take</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>

<span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="n">tokens</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span> <span class="c1">// What is the x slice pointing to?</span>
<span class="p">}</span></code></pre></figure>

<figure class="highlight"><pre><code class="language-gdb" data-lang="gdb">0:000&gt; dq x
0000000d`c9f1ef28  000001d6`26adf044 00000000`00000024
...</code></pre></figure>

<p>Here we are using the <a href="https://docs.microsoft.com/en-us/archive/blogs/windbg/">windbg</a> debugger to examine the actual memory of the variable <code class="language-plaintext highlighter-rouge">x</code>. We see a 64-bit address (<code class="language-plaintext highlighter-rouge">0x000001d626adf044</code>) and a 64-bit length (<code class="language-plaintext highlighter-rouge">0x0000000000000024</code>), which is exactly what we would expect for a string slice. Since our GUIDs are 0x24 (36) characters long, the length is correct.</p>

<p>Let’s look at where the pointer is…pointing.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">0:000&gt; db 000001d6`26adf044
000001d6`26adf044  62 35 35 61 66 37 63 63-2d 65 34 66 32 2d 34 66  b55af7cc-e4f2-4f
000001d6`26adf054  38 65 2d 38 62 30 65 2d-31 38 35 38 32 64 39 66  8e-8b0e-18582d9f
000001d6`26adf064  65 61 62 61 2c 20 20 20-20 20 20 20 20 20 20 37  eaba,          7
000001d6`26adf074  63 65 33 63 62 39 39 2d-38 62 34 33 2d 34 38 36  ce3cb99-8b43-486
000001d6`26adf084  63 2d 62 39 61 38 2d 38-39 62 62 31 38 30 37 36  c-b9a8-89bb18076
000001d6`26adf094  35 31 66 2c 20 0d 0a 31-30 37 64 31 34 66 38 2d  51f, ..107d14f8-
000001d6`26adf0a4  63 33 36 37 2d 34 65 38-36 2d 62 61 37 31 2d 36  c367-4e86-ba71-6
000001d6`26adf0b4  61 39 39 34 64 37 35 61-37 33 34 2c 20 20 20 20  a994d75a734,    </code></pre></figure>

<p>Clearly, it appears to be pointing into our original string, since we can see the expected whitespace and newlines following this. We can look a little bit behind this string and confirm that the original four spaces are there.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">0:000&gt; db 000001d6`26adf044-4
000001d6`26adf040  20 20 20 20 62 35 35 61-66 37 63 63 2d 65 34 66      b55af7cc-e4f
000001d6`26adf050  32 2d 34 66 38 65 2d 38-62 30 65 2d 31 38 35 38  2-4f8e-8b0e-1858
000001d6`26adf060  32 64 39 66 65 61 62 61-2c 20 20 20 20 20 20 20  2d9feaba,       
000001d6`26adf070  20 20 20 37 63 65 33 63-62 39 39 2d 38 62 34 33     7ce3cb99-8b43
000001d6`26adf080  2d 34 38 36 63 2d 62 39-61 38 2d 38 39 62 62 31  -486c-b9a8-89bb1
000001d6`26adf090  38 30 37 36 35 31 66 2c-20 0d 0a 31 30 37 64 31  807651f, ..107d1
000001d6`26adf0a0  34 66 38 2d 63 33 36 37-2d 34 65 38 36 2d 62 61  4f8-c367-4e86-ba
000001d6`26adf0b0  37 31 2d 36 61 39 39 34-64 37 35 61 37 33 34 2c  71-6a994d75a734,</code></pre></figure>

<p>Of course, there are the four spaces.</p>

<p>Finally, just to absolutely prove the point, we can show that the address we just dumped out (<code class="language-plaintext highlighter-rouge">0x000001d626adf040</code>) should be exactly the same as the address our <code class="language-plaintext highlighter-rouge">input</code> string slice variable is pointing to.</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">0:000&gt; dq input
0000000d`c9f1ed18  000001d6`26adf040 00000000`1c314c56</code></pre></figure>

<p>It’s a perfect match (but, we knew that.)</p>

<h3 id="conclusion">Conclusion</h3>
<p>I find it extremely encouraging to work through these sorts of small examples and realize just how dedicated the developers of the Rust language (and core libraries) care about efficiency. When they say ‘zero cost abstractions’ are a core part of the language design, they aren’t kidding.</p>

<p>As I think I made clear in my previous post, I am kind of blown away that you can write what feels like very high-level code in Rust, yet get efficiency and performance that is right up there with hand-optimized, native code.</p>

<p>The brilliant use of things like slices in conjunction with lazily evaluated iterators, as we just saw, steers you into doing things in an efficient way, without imposing cognitive overhead or requiring the use of subtle tricks to squeeze out performance. ‘It just works’ is kind of a cliché in programming circles, but at least in this case…Rust makes it true.</p>

<p>Next time, we will finally unravel this line of code from our example program:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="nn">Uuid</span><span class="p">::</span><span class="nf">parse_str</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="nf">.ok</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="n">s</span><span class="p">))</span></code></pre></figure>

<p>Initially inscrutable to me, working through it will show it all makes sense when we start transitioning from thinking like a C# programmer to thinking like a Rustacean.</p>]]></content><author><name></name></author><category term="Rust," /><category term="C#," /><category term="Programming" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">A C# programmer examines Rust - Part 1</title><link href="/programming,/rust,/c%23/2020/03/06/StartingRust.html" rel="alternate" type="text/html" title="A C# programmer examines Rust - Part 1" /><published>2020-03-06T16:29:00+00:00</published><updated>2020-03-06T16:29:00+00:00</updated><id>/programming,/rust,/c%23/2020/03/06/StartingRust</id><content type="html" xml:base="/programming,/rust,/c%23/2020/03/06/StartingRust.html"><![CDATA[<style type="text/css">
.pretty-table
{
  padding: 0;
  margin: 0;
  border-collapse: collapse;
  border: 1px solid #333;
  font-family: "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
  font-size: 0.9em;
  color: #000;
}

.pretty-table caption
{
  caption-side: bottom;
  font-size: 0.9em;
  font-style: italic;
  text-align: right;
  padding: 0.5em 0;
}

.pretty-table th, .pretty-table td
{
  border: 1px dotted #666;
  padding: 0.5em;
  text-align: left;
  color: #632a39;
}

.pretty-table tr:hover th[scope=row], .pretty-table tr:hover td
{
  background-color: #632a2a;
  color: #fff;
}	
</style>

<p>It’s a dark, gloomy February night and I can hear the rain pelting against the windows of my office, behind me. It’s late, but I’ve lost track of time. I am puzzling over some code, kind of hacking away at it, trying to gain clarity. On my second monitor there is a Discord channel running, which I’m trying to follow while experimenting on my own with the code. The book on my desk is open to Chapter 13 and I occasionally refer to it. Something about the problem I’m working on is not quite clicking, but I can feel it…I’m getting close to understanding.</p>

<p>The book is <a href="https://doc.rust-lang.org/stable/book/"><em>The Rust Programming Language</em></a> and the Discord window is showing the #beginners channel on <a href="https://discord.gg/T2vjnny">The Rust Programming Language Discord Server</a>.</p>

<p>I’ve been dabbling with Rust off and on for about a year, but only in the most ephemeral ways; I’d complete a small code sample from “The Book” every once in a while; I’d play around with some trivial toy code to try and understand simple things, like how to retrieve the actual memory address of a variable so I can go poke around at it in the debugger.</p>

<p>Incidentally:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">fn</span> <span class="n">get_address_value</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">val</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">usize</span> <span class="p">{</span>
    <span class="n">val</span> <span class="k">as</span> <span class="o">*</span><span class="k">const</span> <span class="n">_</span> <span class="k">as</span> <span class="nb">usize</span>
<span class="p">}</span></code></pre></figure>

<p><img src="/images/2020-03-06/rustbook1.jpg" style="width:50%; height:50%;float:right" /></p>

<p>I was curious, quite intrigued, really, but not to the point where I was heavily motivated to dive in and put in the work to actually go deeper.</p>

<p>That changed, however, after I attended the first ever conference on Rust at Microsoft. This was an internal event designed to bring Rust enthusiasts from around the company together; between a series of informative presentations and some great workshops, it was a success that lit a fire to re-kindle my latent interest in the language.</p>

<p>This night, therefore, I was embarking on the start of attempting some actual, real-world programming in Rust. The purpose was purely educational; I had decided to port an existing Command Line Interface (CLI) app, written a few years earlier, to Rust. The app in question was also originally written as a learning project, at the time to teach myself some rudimentary <a href="https://fsharp.org/">F# programming</a>.</p>

<h3 id="why-rust">Why Rust?</h3>
<p>To say Rust is a fascinating language would be an understatement. It represents something novel: a language with the memory-safety guarantees of garbage-collected languages like C#, with the performance and control of systems programming languages like C. It also incorporates expressive, higher-level abstractions borrowed from a variety of other languages, including OCaml, C++, Haskell, Erlang, Scheme, and <a href="https://doc.rust-lang.org/reference/influences.html">more</a>.</p>

<p>Around a year ago, I had no idea what Rust was. I’m sure I had seen references to it, as I read a lot of programming forums and blogs, but with no context I remained unaware of it. It’s like when you learn a new word and start hearing it everywhere; it was always there, but your brain just filtered it out.</p>

<p>Then by chance I stumbled across the video <a href="https://youtu.be/HgtRAbE1nBM">Is It Time to Rewrite the Operating System in Rust?</a>, featuring Bryan Cantrill. I’m a sucker for computing history and this video just had everything to engage my attention: an entertaining speaker, history lessons on systems programming, funny anecdotes and, not least, the intriguing promise of this new programming language called Rust.</p>

<p>So, I got interested…and here we are a year late on a stormy night, cozy in our office and wrestling with Rust.</p>

<h3 id="a-small-programming-problem">A small programming problem</h3>
<p>The F# program I decided to port to Rust can take several types of input. One possible input is a list of Globally Unique Identifiers (GUIDs), separated by commas. Therefore, we have the following small programming problem to solve:</p>

<blockquote>
  <p>Given a string which contains comma-separated values, transform it into a list of Globally Unique Identifiers (GUIDs) if and only if every value is a valid GUID. Ignore whitespace and newlines.</p>
</blockquote>

<p>This is not a sophisticated problem, but it is rather typical of the sort of pragmatic problem we have to solve all of the time. This was the problem I was working on solving in Rust, as a first-step to porting my F# program over.</p>

<p>To test any candidate solution, I created ten million GUIDs and wrote them to disk, separating them with commas and random whitespace, including newlines. Why so many? Because trivial amounts of data are just so…uninteresting. You find useful insights when you push your data into the millions, such as bad algorithmic choices that are way too slow, or stack overflows because you assumed memory is boundless, or other interesting issues that my former professional-software-tester self still loves to find.</p>

<p>I know we just wasted ten million GUIDs, <a href="http://www.technofattie.com/2013/04/15/but-what-if-we-run-out-of-guids.html">a precious resource that we need to worry about running out of (#SaveTheGuids)</a>, I hope you can get past my wanton waste.</p>

<p>We should test the invalid case as well, since we stipulated we must only produce a list if all values are valid GUIDs. To test this, we make a copy of the first file and down near the bottom of the mass of GUIDs, newlines and spaces, we can do something like this:</p>

<pre>
53d33661-95f4-4410-8005-274cb477c318,                  fd9cef68-7783-449e-bd02-f6aa1591de84,
6160607e-c770-40ab-94be-ddd5dd092300,        4a35fac7-6768-4c57-8a06-42b96c5b3438,7864a0db-1707-4358-b877-594bc4648f6b,
68d2091f-e194-4361-a1a4-f38332b1ab13,    
979cde21-0b24-433e-9790-8d52daf125fd,  
83a36f67-db75-4b8f-92a8-369001416a5e,            <span style="color:red">I_AM_NOT_A_GUID</span>,3f4d46ca-0b38-4f65-b915-d280187bcc4f,  
71ba44b1-eb6d-472c-841a-56f08f08ec87,                   d12be9e7-2eb6-4841-b9d0-275db66a4d6e,
f9942cff-c51e-4d48-9b8e-225edc397528,   
</pre>

<p>Perfect, we now have two files: one that should produce a list of ten million GUIDs, the other that should produce some result indicating the input was not a valid, well-formed bowl of GUID soup.</p>

<h4 id="solving-the-problem-with-imperative-coding---c">Solving the problem with imperative coding - C#.</h4>
<p>Two popular paradigms when solving a programming problem are <em>imperative</em>, where the code details the specific steps the computer should execute in order to arrive at a solution, and <em>declarative</em>, where the code specifies the desired logic without detailing every specific step.</p>

<p>Let’s first look at solving our small programming problem using an imperative style.</p>

<p>A straight-forward implementation in something like C# might look like the following:</p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">static</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">ExtractIds</span><span class="p">(</span><span class="kt">string</span> <span class="n">input</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="n">result</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>

    <span class="kt">string</span><span class="p">[]</span> <span class="n">tokens</span> <span class="p">=</span> <span class="n">input</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="s">","</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">tokens</span><span class="p">.</span><span class="n">Length</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
    <span class="p">{</span>
        <span class="kt">string</span> <span class="n">trimmed</span> <span class="p">=</span> <span class="n">tokens</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="nf">Trim</span><span class="p">();</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">Guid</span><span class="p">.</span><span class="nf">TryParse</span><span class="p">(</span><span class="n">trimmed</span><span class="p">,</span> <span class="k">out</span> <span class="n">_</span><span class="p">))</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">result</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;();</span>
            <span class="p">}</span>

            <span class="n">result</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">trimmed</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">else</span>
        <span class="p">{</span>
            <span class="n">result</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<h4 id="solving-the-problem-with-declarative-coding---rust">Solving the problem with declarative coding - Rust.</h4>
<p>While Rust certainly supports the constructs like loops and if-else conditionals that are often used as the primary tools in imperative programming, the examples I have seen of idiomatic Rust code tend towards the more declarative style favored by functional programming languages.</p>

<p>Here is the same solution, written in Rust using a declarative style:</p>

<figure class="highlight"><pre><code class="language-rust" data-lang="rust"><span class="k">fn</span> <span class="nf">extract_ids</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="nb">str</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
    <span class="n">input</span>
        <span class="nf">.split</span><span class="p">(</span><span class="sc">','</span><span class="p">)</span>
        <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="n">s</span><span class="nf">.trim</span><span class="p">())</span>
        <span class="nf">.map</span><span class="p">(|</span><span class="n">s</span><span class="p">|</span> <span class="nn">Uuid</span><span class="p">::</span><span class="nf">parse_str</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="nf">.ok</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="n">s</span><span class="p">))</span>
        <span class="nf">.collect</span><span class="p">()</span>
<span class="p">}</span></code></pre></figure>

<p>(Thanks to the folks on the Discord channel who helped me find my way to this particular solution.)</p>

<h4 id="comparison">Comparison</h4>
<p>Which code sample is better? That depends, of course, on your criteria: better in terms of readability? Better in terms of runtime performance? Better in terms of likelihood to have bugs?</p>

<p>I would argue the Rust code is better in all of those criteria, once you have a minimum fluency in functional programming and Rust syntax.</p>

<p>Programmers love to argue about useless trivia, like what it means for a language to be ‘low-level’ or ‘high-level’. Is C# a low-level programming language? Is Rust?</p>

<p>Well, come on, just look at that Rust code. Does that look like a ‘low-level’ language to you???</p>

<p>The amazing promise of Rust, however, is that you can write declarative code that looks decidedly like it came from a high-level language, yet get the runtime performance of languages like C. Indeed, the code above compiles down to very efficient machine code.</p>

<p>We can look, albeit informally, at the performance characteristics of these two pieces of code in a bit. I’d like to point something else out, which is probably far more important than runtime performance. In most applications, code like the above is not really going to need to process ten million GUIDs. It will be processing a few hundred or maybe a few thousand, and the couple of milliseconds of difference in performance is unlikely to matter.</p>

<p>What does matter? Bugs. Correctness. Confidence in the code.</p>

<p>Now, we’re looking at a rather trivial problem and solution, but look closely at both versions of the code and ask yourself: which is more likely to have bugs? Which has mutable state? Which has possible null dereferences?</p>

<p>The Rust code is entirely immune from entire classes of bugs, which is just…awesome. You can’t pass it input that is invalid and will make the code crash (‘panic’, in Rust lingo) because, for instance, there is no ‘null’ in Rust. The Rust function has no mutable state and no possibility of null references. (Everything is immutable by default in Rust, the exact opposite of C#).</p>

<p>In the C# code, it crashes if ‘input’ happens to be null. Oops. There is no such possibility in the Rust version: the input is guaranteed by the compiler to be valid.</p>

<p>There is no possibility of an off-by-one error in the Rust code, for instance because we messed up some loop variable. The C# compiler, on the other hand, won’t catch that <code class="language-plaintext highlighter-rouge">for (int i = 0; i &lt;= tokens.Length; i++)</code> is going to crash at runtime, if we had mistakenly written it that way. In fact, in my original version of the code I wrote <code class="language-plaintext highlighter-rouge">for (int i = 0; i &lt;= input.Length; i++&gt;)</code> (using the ‘input’ variable instead of ‘tokens’) and it crashed. Oops.</p>

<p>Now expand this trivial, almost silly little example to code that is thousands or tens of thousands or more lines of code. Which style of coding is going to give you more confidence? I know for me, knowing that certain kinds of bugs are completely impossible is a huge win.</p>

<p>(Don’t even get me started on Rust’s amazing concurrency guarantees…)</p>

<h4 id="solving-the-problem-with-declarative-coding---c">Solving the problem with declarative coding - C#.</h4>
<p>We can take a stab at re-writing our C# code to use a declarative style, which the language supports via Language Integrated Query (LINQ).</p>

<p>Here’s the code:</p>

<figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">static</span> <span class="n">List</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">ExtractIdsDeclarative</span><span class="p">(</span><span class="kt">string</span> <span class="n">input</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">input</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">input</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="s">","</span><span class="p">).</span><span class="nf">Select</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="n">s</span><span class="p">.</span><span class="nf">Trim</span><span class="p">());</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="nf">Any</span><span class="p">(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="p">!</span><span class="n">Guid</span><span class="p">.</span><span class="nf">TryParse</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="k">out</span> <span class="n">_</span><span class="p">)))</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>

<p>By adding some parameter validation and using a declarative style for mapping the input to normalized tokens and validating the result, it’s better. There is less to go wrong, at least.</p>

<p>There’s a problem, however. The less-error-prone declarative C# version is significantly slower to execute, as we shall see shortly. We paid for that additional safety by introducing runtime inefficiency.</p>

<p>In most languages, higher-level abstractions result in a trade-off: higher runtime costs in terms of execution time or memory usage or both.</p>

<p>Rust is one of those rare exceptions where using higher-level abstractions often does not result in any runtime penalty at all.</p>

<h4 id="quick-performance-comparison---building-and-validating-a-list-of-ten-million-guids">Quick performance comparison - building and validating a list of ten million GUIDs</h4>
<p>A quick-and-dirty performance comparison, just for fun. This is not a scientific, rigorous benchmark, just a quick measurement of the code snippets above.</p>

<p>(Note that I excluded stats for executing against the ‘invalid’ input file because the performance data was essentially the same.)</p>

<table width="680" border="1" class="pretty-table">
  <caption>
    Performance comparison
  </caption>
  <tbody>
    <tr>
      <th width="138" scope="col">Test</th>
      <th width="260" scope="col">Elapsed Milliseconds</th>
      <th width="260" scope="col">Peak Memory Usage</th>
      <th width="260" scope="col">Memory Usage After Processing</th>
      <th width="260" scope="col">Memory Usage After Gen2 GC</th>
    </tr>
    <tr>
      <td>C# - Imperative</td>
      <td>5444</td>
      <td>3.4GB</td>
      <td>3.4GB</td>
      <td>2.05GB</td>
    </tr>
    <tr>
      <td>C# - Declarative</td>
      <td>6785</td>
      <td>3.2GB</td>
      <td>3.2GB</td>
      <td>1.9GB</td>
    </tr>
    <tr>
      <td>Rust - Declarative</td>
      <td>2851</td>
      <td>859MB</td>
      <td>464MB</td>
      <td>N/A</td>
    </tr>
  </tbody>
</table>

<p>The .NET code, being garbage collected, has to of course wait for the GC to come around and do it’s thing, so I forced a gen2 GC to get an approximately apples-to-apples comparison of the memory usage after all of the processing was done.</p>

<h3 id="in-summary">In summary</h3>
<p>I’m a Rust beginner, so as I start exploring the language I am bound to get some things terribly wrong, make a lot of mistakes and learn a lot in the process. If you see anything egregiously incorrect, please drop me a note so I can address it in a follow-up post.</p>

<p>This post barely scratched the surface of why I think Rust has an amazing future, both for systems programming and for programming higher up the technology stack, like in the cloud services I work on for a living. Is Rust going to imminently replace C# and the amazing open-source .NET ecosystem? Of course not. Does it have a role in solving hard, real-world performance and scale problems that are hard to achieve within a managed (garbage collected) environment? I think that it obviously does.</p>

<p>We took a very small code sample for a trivial coding problem and used that to explore some interesting properties of Rust. First, we saw how the language gives us a declarative way of coding that makes introducing common classes of bugs impossible. Second, we saw how much more efficient the Rust code is at runtime over a garbage-collected language like C#, both in terms of speed and memory usage.</p>

<p>If you didn’t understand the Rust code at all, I don’t blame you. Rust is not easy to grasp if you haven’t seen it before. The learning curve is high, there are many complex features and the code itself tends to be very ‘symbol dense’ and non-obvious. It’s sort of like an anti-Python. What you gain for all of that complexity, however, is performance, correctness and control down to the lowest levels.</p>

<p>I am hoping to try to ‘unpack’ this intriguing little code sample in a future blog post, to go through and explain what’s going on and how it works, as well as to document what else I have learned as I continue poking around in the Rust landscape.</p>]]></content><author><name></name></author><category term="Programming," /><category term="Rust," /><category term="C#" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Software tools I love - Part 5</title><link href="/programming,/blogging/2020/01/09/ToolsICantLiveWithoutPart5.html" rel="alternate" type="text/html" title="Software tools I love - Part 5" /><published>2020-01-09T10:30:00+00:00</published><updated>2020-01-09T10:30:00+00:00</updated><id>/programming,/blogging/2020/01/09/ToolsICantLiveWithoutPart5</id><content type="html" xml:base="/programming,/blogging/2020/01/09/ToolsICantLiveWithoutPart5.html"><![CDATA[<style>
.cat1 {
    color:#1e3799;
}
</style>

<p>This is part 5 in my series where I document my process for going from a brand new computer to one that has the basic set of tools I want present to feel productive.</p>

<p>For previous installements, see:</p>

<p><a href="/programming/2019/12/08/ToolsICantLiveWithout.html">Part 1</a>,
<a href="/programming/2019/12/16/ToolsICantLiveWithoutPart2.html">Part 2</a>,
<a href="/programming/2019/12/17/ToolsICantLiveWithoutPart3.html">Part 3</a>,
<a href="/programming/2019/12/19/ToolsICantLiveWithoutPart4.html">Part 4</a></p>

<hr />

<p>So far we have:</p>

<ul>
  <li>Installed <a href="https://blogs.windows.com/blog/tag/windows-insider-program/">The latest Windows 10 Insider Build</a>.</li>
  <li>Installed <a href="https://devblogs.microsoft.com/commandline/introducing-windows-terminal/">Windows Terminal</a>.</li>
  <li>Installed the <a href="https://devblogs.microsoft.com/commandline/cascadia-code/">Cascadia Code font</a>.</li>
  <li>Installed <a href="https://code.visualstudio.com/">Visual Studio Code</a>.</li>
  <li>Installed <a href="https://git-scm.com/downloads">Git</a>.</li>
  <li>Installed the <a href="https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&amp;rel=16">Visual Studio Build Tools 2019</a>.</li>
  <li>Configured Windows Terminal to have profiles for building both native and managed code.</li>
  <li>Verified we can build simple C and C# programs from the terminal.</li>
  <li>Installed <a href="https://github.com/powershell/powershell">PowerShell Core</a> on Windows.</li>
  <li>Installed the <a href="https://github.com/ecsousa/PSColors">PSColors</a> PowerShell module</li>
  <li>Started building a set of helper functions in our PowerShell $profile</li>
  <li>Installed the <a href="https://docs.microsoft.com/en-us/windows/wsl/about">Windows Subsystem for Linux (WSL)</a></li>
  <li>Installed and updated <a href="http://releases.ubuntu.com/18.04/">Ubuntu 18.04</a> on WSL.</li>
  <li>Installed the <a href="https://docs.microsoft.com/en-us/dotnet/core/install/sdk?pivots=os-linux">.NET Core SDK</a> on our WSL environment.</li>
  <li>Installed <a href="https://github.com/powershell/powershell">PowerShell Core</a> on WSL.</li>
  <li>Installed the <a href="https://rustup.rs/">Rust toolchain</a> and <a href="https://github.com/rust-lang/cargo/">Cargo</a> so we can build and install projects written in Rust.</li>
  <li>Installed <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> to allow fast searching for text in files.</li>
  <li>Installed <a href="https://github.com/facebookincubator/fastmod">fastmod</a> to perform bulk search and replace / refactorings from the terminal.</li>
  <li>Installed <a href="https://starship.rs/">Starship</a> to get an awesome looking terminal prompt.</li>
</ul>

<h2 id="blogging-with-github-pages">Blogging with GitHub Pages</h2>
<p>In today’s post we are going to look at what we need to do to set up our new machine for writing blog posts using <a href="https://pages.github.com/">GitHub Pages</a>.</p>

<h3 id="installing-a-ruby-development-environment">Installing a Ruby development environment</h3>
<p><img src="/images/2020-01-09/1024px-Ruby_logo.svg.png" style="width:15%; height:15%;float:right;margin:20px;" /></p>

<p>I’m not a Ruby programmer, however we will need a Ruby development environment to best take advantage of how GitHub supports acting as a blogging platform.</p>

<p>GitHub pages allows you to write blog posts by simply commiting markdown or HTML files to your GitHub repo at username.github.io. GitHub Pages integrates tightly with the static website generating tool <a href="https://jekyllrb.com/">Jekyll</a>. The markdown files you author are converted to static HTML by Jekyll, and then published as blog posts by GitHub Pages when the content is commited and pushed to the remote repo.</p>

<p>While it is not necessary to use Jekyll to generate your GitHub Pages blog, it removes a lot of manual work and is therefore the simplest way to get things done with minimal fuss. You still have complete control over customizing the resulting site, so even if you want to hand-code your own static HTML pages and eschew using markdown, you can.</p>

<p>Jekyll is written using the <a href="https://www.ruby-lang.org/en/">Ruby</a> programming language, so we will need to start by installing the Ruby toolchain to allow us to run a local Jekyll server. This allows previewing what our blog looks like before publishing it.</p>

<p>For a detailed tutorial on setting up a Jekyll blog, please see <a href="https://help.github.com/en/github/working-with-github-pages/setting-up-a-github-pages-site-with-jekyll">this page</a> on github.com, which goes into great detail.</p>

<p>For the purpose of this post, we already have an existing blog on github.io, but we need to get the necessary tools installed on the new computer we have been setting up. In other words, this is the cliff-notes version.</p>

<p>To install the Ruby development tools on Windows, we follow the <a href="https://www.ruby-lang.org/en/documentation/installation/">instructions here</a>, which direct us to <a href="https://rubyinstaller.org/">rubyinstaller.org</a> and in particular (as of this writing) the <a href="https://github.com/oneclick/rubyinstaller2/releases/download/RubyInstaller-2.6.5-1/rubyinstaller-devkit-2.6.5-1-x64.exe">2.6.5.-1-x64 setup package with devkit</a>.</p>

<p>This is a standard Windows setup program, so we simple execute it and wait a few minutes for the installation to complete.</p>

<p><img src="/images/2020-01-09/ruby_install1.png" alt="install1" /></p>

<h4 id="configuring-a-ruby-terminal-environment">Configuring a Ruby terminal environment</h4>
<p>Once the install finishes there will be a ‘Start Command Prompt with Ruby’ shortcut on the Start Menu. However, that will launch the Ruby environment running under the old, ugly and not-very-fun conhost.exe. As early adopters of the new and vastly more beautiful and exciting <a href="https://github.com/Microsoft/Terminal">Windows Terminal</a>, we will set up a separate terminal profile to use for blogging.</p>

<p>Step 1: create a wrapper script to execute the Ruby initialization script and change to a specified folder. We do this by saving the following under %userprofile%\tools\rubyprompt.cmd:</p>

<figure class="highlight"><pre><code class="language-batch" data-lang="batch">@echo <span class="na">off</span>
<span class="k">call</span> <span class="kd">C</span>:\Ruby26<span class="na">-x</span><span class="m">64</span>\bin\setrbvars.cmd
<span class="nb">cd</span> <span class="na">/d </span><span class="s2">"</span><span class="err">%</span><span class="s2">1"</span>
<span class="kd">pwsh</span></code></pre></figure>

<p>(Obviously replace the path to setrbvars.cmd to the location to which you installed Ruby.)</p>

<p>Now we can create a Windows Terminal profile that will invoke the above script.</p>

<p>In Windows Terminal we press ctrl+, (control plus the comma character) to open the settings editor, and add a new section like this:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"guid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{9a544494-e45e-46a3-af38-2683f77c65f5}"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"hidden"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Blog"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"commandline"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"cmd.exe /k </span><span class="se">\"</span><span class="s2">%userprofile%</span><span class="se">\\</span><span class="s2">tools</span><span class="se">\\</span><span class="s2">rubyprompt.cmd c:</span><span class="se">\\</span><span class="s2">src</span><span class="se">\\</span><span class="s2">github</span><span class="se">\\</span><span class="s2">Treit</span><span class="se">\\</span><span class="s2">treit.github.io</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"fontFace"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cascadia Mono PL"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"colorScheme"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Campbell"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"icon"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"%userprofile%</span><span class="se">\\</span><span class="s2">Pictures</span><span class="se">\\</span><span class="s2">fountainpen1.png"</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>As usual, be sure to generate a unique Guid for the new profile.</p>

<p>Now when we want to write blog posts we can simply open the Windows Terminal profile called ‘Blog’ and it will initialize our environment appropriately.</p>

<p><img src="/images/2020-01-09/blog_menu1.png" alt="menu1" /></p>

<h4 id="customzing-our-terminal-color-scheme-for-blogging">Customzing our terminal color scheme for blogging</h4>
<p>We set the ‘colorScheme’ setting to the popular ‘Campbell’ setting that comes with Windows Terminal, but there are endless possibilities for customizing the color scheme to match our own aesthetic tastes. I like my blogging environment to stand out from my other coding environments, so let’s come up with a different scheme.</p>

<p>We could write out own, but luckily there exists a huge assortment of existing, popular terminal color schemes available on github. These can be found at the <a href="https://github.com/mbadolato/iTerm2-Color-Schemes">iTerm2-Color-Schemes repo</a>. <strong>Warning</strong>: visiting this site could lead to many hours of wasted productivity as you try out scheme after scheme in your terminal!</p>

<p>Let’s say we like the look of the <a href="https://github.com/mbadolato/iTerm2-Color-Schemes/blob/master/windowsterminal/Belafonte%20Day.json">Belafonte Day</a> color scheme and want to try that out for our blogging terminal. We simply download the json and add it to the ‘schemes’ section of our terminal settings file:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="nl">"schemes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Belafonte Day"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"black"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#20111b"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"red"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#be100e"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"green"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#858162"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"yellow"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#eaa549"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"blue"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#426a79"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"purple"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#97522c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"cyan"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#989a9c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"white"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#968c83"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightBlack"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#5e5252"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightRed"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#be100e"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightGreen"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#858162"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightYellow"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#eaa549"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightBlue"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#426a79"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightPurple"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#97522c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightCyan"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#989a9c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"brightWhite"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#d5ccba"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"background"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#d5ccba"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"foreground"</span><span class="p">:</span><span class="w"> </span><span class="s2">"#45373c"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="err">,</span></code></pre></figure>

<p>…and then set that as the colorScheme settings for our Blog profile:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"guid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{9a544494-e45e-46a3-af38-2683f77c65f5}"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"hidden"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Blog"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"commandline"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"cmd.exe /k </span><span class="se">\"</span><span class="s2">%userprofile%</span><span class="se">\\</span><span class="s2">tools</span><span class="se">\\</span><span class="s2">rubyprompt.cmd c:</span><span class="se">\\</span><span class="s2">src</span><span class="se">\\</span><span class="s2">github</span><span class="se">\\</span><span class="s2">Treit</span><span class="se">\\</span><span class="s2">treit.github.io</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"fontFace"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cascadia Mono PL"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"colorScheme"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Belafonte Day"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"icon"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"%userprofile%</span><span class="se">\\</span><span class="s2">Pictures</span><span class="se">\\</span><span class="s2">fountainpen1.png"</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>The resulting prompt will end up looking something like this:</p>

<p><img src="/images/2020-01-09/color_scheme_1.png" alt="color1" /></p>

<h3 id="installing-jekyll-and-bundler">Installing Jekyll and Bundler</h3>
<p>Ok, we have a Ruby environment installed and we have a nice Windows Terminal profile that we will use for blogging, complete with a new color scheme.</p>

<p>Next we will need to install Jekyll and Bundler. Jekyll, as we have mentioned, is the static site generator that will generate the final HTMl for our blog, and Bundler is a popular Ruby package manager that ensures the correct versions of Ruby packages (called ‘gems’) are installed.</p>

<p>From our Blog terminal that we just configued, we simply run:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">gem</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">jekyll</span><span class="w"> </span><span class="nx">bundler</span></code></pre></figure>

<p>Once the install completes we need to run bundler to install any missing gems that we need, as specified in the gemfile for our blog. From the root folder of our blog, which we have already cloned from GitHub, we just run:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">bundle</span><span class="w"> </span><span class="nx">install</span></code></pre></figure>

<p>Once the install command finishes, we’re done! Now we can start our local blog server and view our local blog content as we work on new posts, by simply running:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">bundle</span><span class="w"> </span><span class="nx">exec</span><span class="w"> </span><span class="nx">jekyll</span><span class="w"> </span><span class="nx">serve</span></code></pre></figure>

<p>Of course the above assumes you already have a working Jekyll blog. If you have never set up a blog using Jekyll, <a href="https://jekyllrb.com/docs/">the documentation</a> has some great tutorials to get you started.</p>

<h2 id="conclusion">Conclusion</h2>
<p>We’ve completed another step on setting up a development workstation from scratch, this time installing the tools needed to enjoy authoring blog posts using GitHub Pages. At this point we are pretty close to having all of the core tools and settings configured to feel productive and happy! However, I think I probably have at least another couple of tools I can’t live without that I will share in a final upcoming post or two.</p>

<p>Happy new year!</p>]]></content><author><name></name></author><category term="Programming," /><category term="Blogging" /><summary type="html"><![CDATA[This is part 5 in my series where I document my process for going from a brand new computer to one that has the basic set of tools I want present to feel productive.]]></summary></entry><entry><title type="html">Software tools I love - Part 4</title><link href="/programming/2019/12/19/ToolsICantLiveWithoutPart4.html" rel="alternate" type="text/html" title="Software tools I love - Part 4" /><published>2019-12-19T13:00:00+00:00</published><updated>2019-12-19T13:00:00+00:00</updated><id>/programming/2019/12/19/ToolsICantLiveWithoutPart4</id><content type="html" xml:base="/programming/2019/12/19/ToolsICantLiveWithoutPart4.html"><![CDATA[<style>
.cat1 {
    color:#1e3799;
}
</style>

<p>This is part 4 in my series where I document my process for going from a brand new computer to one that has the basic set of tools I want present to feel productive.</p>

<p>For previous installements, see:</p>

<p><a href="/programming/2019/12/08/ToolsICantLiveWithout.html">Part 1</a>,
<a href="/programming/2019/12/16/ToolsICantLiveWithoutPart2.html">Part 2</a>,
<a href="/programming/2019/12/17/ToolsICantLiveWithoutPart3.html">Part 3</a></p>

<hr />

<p>So far we have:</p>

<ul>
  <li>Installed <a href="https://blogs.windows.com/blog/tag/windows-insider-program/">The latest Windows 10 Insider Build</a>.</li>
  <li>Installed <a href="https://devblogs.microsoft.com/commandline/introducing-windows-terminal/">Windows Terminal</a>.</li>
  <li>Installed the <a href="https://devblogs.microsoft.com/commandline/cascadia-code/">Cascadia Code font</a>.</li>
  <li>Installed <a href="https://code.visualstudio.com/">Visual Studio Code</a>.</li>
  <li>Installed <a href="https://git-scm.com/downloads">Git</a>.</li>
  <li>Installed the <a href="https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&amp;rel=16">Visual Studio Build Tools 2019</a>.</li>
  <li>Configured Windows Terminal to have profiles for building both native and managed code.</li>
  <li>Verified we can build simple C and C# programs from the terminal.</li>
  <li>Installed <a href="https://github.com/powershell/powershell">PowerShell Core</a> on Windows.</li>
  <li>Installed the <a href="https://github.com/ecsousa/PSColors">PSColors</a> PowerShell module</li>
  <li>Started building a set of helper functions in our PowerShell $profile</li>
  <li>Installed the <a href="https://docs.microsoft.com/en-us/windows/wsl/about">Windows Subsystem for Linux (WSL)</a></li>
  <li>Installed and updated <a href="http://releases.ubuntu.com/18.04/">Ubuntu 18.04</a> on WSL.</li>
  <li>Installed the <a href="https://docs.microsoft.com/en-us/dotnet/core/install/sdk?pivots=os-linux">.NET Core SDK</a> on our WSL environment.</li>
  <li>Installed <a href="https://github.com/powershell/powershell">PowerShell Core</a> on WSL.</li>
</ul>

<h2 id="installing-the-rust-toolchain-on-windows">Installing the Rust toolchain on Windows</h2>
<p><img src="/images/2019-12-19/rust_logo1.png" style="width:25%; height:25%;float:right;margin:20px;" /></p>

<p>The <a href="https://www.rust-lang.org/">Rust</a> programming language is fascinating. I’ve heard about it for a few years but really had no idea what it was all about. Then I happened across the highly entertaining video <a href="https://www.youtube.com/watch?v=HgtRAbE1nBM">Is It Time to Rewrite the Operating System in Rust?</a> and my curiosity was strongly piqued. A language that provides both memory-safety and native-code levels of performance? With modern language features like exhaustive pattern matching and iterators? This sounds like the kind of programming language I’ve dreamed about.</p>

<p>In fairness I have yet to do any real programming in Rust, but it’s high on my to-do list. As part of researching the language, however, I came across a few extremely neat tools that happened to be written in Rust.</p>

<p>So even if we aren’t going to be doing a lot of Rust coding imminently, installing tools written in Rust gives us a great excuse to get a Rust programming environment bootstrapped onto the new computer we are setting up.</p>

<p>We use a tool called <a href="https://rustup.rs">rustup</a> to get Rust installed. On Windows this is done by downloading and executing the rustup-int.exe program. This takes a few minutes to install, but once finished we should be ready to build our own Rust programs.</p>

<h4 id="creating-a-rust-terminal-profile">Creating a Rust terminal profile</h4>
<p>I like to have my different programming environments have their own Windows Terminal profile, so let’s create one for when we are doing Rust programming.</p>

<p>I am going to make use of the devprompt_native.cmd wrapper that we created in a previous post to add the necessary paths to the C compilers and other native code development tools we installed. This will give the flexibility to build C/C++ programs as well as Rust programs from the same terminal window, if necessary.</p>

<p>Here is the profile I added:</p>

<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
    </span><span class="nl">"guid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{186f6a99-0cc4-4db4-9d49-47c862b718a3}"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Rust"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"colorScheme"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Campbell"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"commandline"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"cmd.exe /k </span><span class="se">\"</span><span class="s2">c:</span><span class="se">\\</span><span class="s2">users</span><span class="se">\\</span><span class="s2">mtreit</span><span class="se">\\</span><span class="s2">tools</span><span class="se">\\</span><span class="s2">devprompt_native.cmd c:</span><span class="se">\\</span><span class="s2">src</span><span class="se">\\</span><span class="s2">git</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"cursorColor"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"#71F6ED"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"cursorShape"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"emptyBox"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"fontFace"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cascadia Code PL"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"backgroundImage"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"C:/Users/mtreit/Pictures/rustacean.png"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"backgroundImageOpacity"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="mf">0.15</span><span class="p">,</span><span class="w">
    </span><span class="nl">"backgroundImageStretchMode"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"uniformToFill"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"icon"</span><span class="p">:</span><span class="s2">"c:/users/mtreit/Pictures/cuddlyferris-300x224.png"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"hidden"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="p">}</span></code></pre></figure>

<p>Now we have a profile we can launch from Windows Terminal:
<img src="/images/2019-12-19/RustPrompt1.png" alt="RustPrompt" /></p>

<p>It looks like this when running:
<img src="/images/2019-12-19/RustPrompt2.png" alt="RustPrompt" /></p>

<p>Let’s verify we can build a Rust program! Rust uses a package manager called <a href="https://github.com/rust-lang/cargo/">Cargo</a>, which is installed as part of the Rust toolchain. In general, we use Cargo to build Rust projects, rather than invoking the Rust compiler (rustc) directly.</p>

<p>We can use Cargo to initialize a new project and build it for us, so let’s do that and ensure everything is working properly:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">C:\src\git</span><span class="err">&gt;</span><span class="nx">cargo</span><span class="w"> </span><span class="nx">new</span><span class="w"> </span><span class="nx">hello_rust</span><span class="w">
     </span><span class="n">Created</span><span class="w"> </span><span class="nx">binary</span><span class="w"> </span><span class="p">(</span><span class="n">application</span><span class="p">)</span><span class="w"> </span><span class="err">`</span><span class="n">hello_rust</span><span class="se">` </span><span class="nx">package</span><span class="w">

</span><span class="n">C:\src\git</span><span class="err">&gt;</span><span class="nx">cd</span><span class="w"> </span><span class="o">.</span><span class="nx">\hello_rust\</span><span class="w">

</span><span class="n">C:\src\git\hello_rust</span><span class="err">&gt;</span><span class="nx">cat</span><span class="w"> </span><span class="o">.</span><span class="nx">\src\main.rs</span><span class="w">
</span><span class="n">fn</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">println</span><span class="o">!</span><span class="p">(</span><span class="s2">"Hello, world!"</span><span class="p">);</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">C:\src\git\hello_rust</span><span class="err">&gt;</span><span class="nx">cargo</span><span class="w"> </span><span class="nx">run</span><span class="w">
   </span><span class="n">Compiling</span><span class="w"> </span><span class="nx">hello_rust</span><span class="w"> </span><span class="nx">v0.1.0</span><span class="w"> </span><span class="p">(</span><span class="n">C:\src\git\hello_rust</span><span class="p">)</span><span class="w">
    </span><span class="n">Finished</span><span class="w"> </span><span class="nx">dev</span><span class="w"> </span><span class="p">[</span><span class="n">unoptimized</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">debuginfo</span><span class="p">]</span><span class="w"> </span><span class="nx">target</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="mf">0.55</span><span class="n">s</span><span class="w">
     </span><span class="nx">Running</span><span class="w"> </span><span class="se">`t</span><span class="nx">arget\debug\hello_rust.exe</span><span class="se">`
</span><span class="nx">Hello</span><span class="p">,</span><span class="w"> </span><span class="nx">world</span><span class="o">!</span></code></pre></figure>

<p>Perfect! We can now build Rust programs, as well as use Cargo to install existing Rust programs that are part of the Rust ecosystem.</p>

<h2 id="installing-the-rust-toolchain-on-ubuntu--wsl">Installing the Rust toolchain on Ubuntu / WSL</h2>
<p>We’ve installed our Rust programming environment on Windows; now let’s do the same in our Linux (WSL) install on the same machine.</p>

<p>Here we can run the following to get the Rust toolchain bootstrapped:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">curl</span><span class="w"> </span><span class="nt">--proto</span><span class="w"> </span><span class="s1">'=https'</span><span class="w"> </span><span class="nt">--tlsv1</span><span class="o">.</span><span class="nf">2</span><span class="w"> </span><span class="nt">-sSf</span><span class="w"> </span><span class="nx">https://sh.rustup.rs</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">sh</span></code></pre></figure>

<p>Again, this will take a few minutes to install, after which we can run the same cargo commands in our Bash prompt to verify everything is working as expected.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">mtreit@mtreit3:/mnt/c/Users/mtreit<span class="nv">$ </span><span class="nb">cd</span> /mnt/c/src/git
mtreit@mtreit3:/mnt/c/src/git<span class="nv">$ </span>cargo new hello_rust_wsl
     Created binary <span class="o">(</span>application<span class="o">)</span> <span class="sb">`</span>hello_rust_wsl<span class="sb">`</span> package
mtreit@mtreit3:/mnt/c/src/git<span class="nv">$ </span><span class="nb">cd </span>hello_rust
hello_rust/     hello_rust_wsl/
mtreit@mtreit3:/mnt/c/src/git<span class="nv">$ </span><span class="nb">cd </span>hello_rust
hello_rust/     hello_rust_wsl/
mtreit@mtreit3:/mnt/c/src/git<span class="nv">$ </span><span class="nb">cd </span>hello_rust_wsl/
mtreit@mtreit3:/mnt/c/src/git/hello_rust_wsl<span class="nv">$ </span><span class="nb">ls
</span>Cargo.toml  src
mtreit@mtreit3:/mnt/c/src/git/hello_rust_wsl<span class="nv">$ </span>cargo run
   Compiling hello_rust_wsl v0.1.0 <span class="o">(</span>/mnt/c/src/git/hello_rust_wsl<span class="o">)</span>
error: linker <span class="sb">`</span>cc<span class="sb">`</span> not found
  |
  <span class="o">=</span> note: No such file or directory <span class="o">(</span>os error 2<span class="o">)</span>

error: aborting due to previous error

error: could not compile <span class="sb">`</span>hello_rust_wsl<span class="sb">`</span>.</code></pre></figure>

<p>Oh no! It didn’t work.</p>

<p>However, the error message is pretty clear: we are missing some necessary build tools, such as the expected linker.</p>

<p>We installed (in a previous installment of this series) the Microsoft build tools on Windows, but we did not install the Linux equivalents when we set up our WSL environment. Luckily, this is an easy fix. We just need to install the core <a href="https://gcc.gnu.org/">gcc</a> tools, which we can do as follows:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>apt <span class="nb">install </span>build-essential</code></pre></figure>

<p>After the install completes, we can try again:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">mtreit@mtreit3:/mnt/c/src/git/hello_rust_wsl<span class="nv">$ </span>cargo run
   Compiling hello_rust_wsl v0.1.0 <span class="o">(</span>/mnt/c/src/git/hello_rust_wsl<span class="o">)</span>
    Finished dev <span class="o">[</span>unoptimized + debuginfo] target<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>0.60s
     Running <span class="sb">`</span>target/debug/hello_rust_wsl<span class="sb">`</span>
Hello, world!</code></pre></figure>

<p>Great, we now have the Rust toolchain working on both Windows and WSL.</p>

<h2 id="the-joy-of-ripgrep">The joy of ripgrep</h2>
<p>I don’t know about you, but I spend a lot of time on the command-line searching for text in files on disk, typically source code files. For more than a decade I always used the rudimentary <code class="language-plaintext highlighter-rouge">findstr</code> command that comes with Windows. It did the job, but it was not particularly fast and has a very minimal feature set.</p>

<p>Then I discovered <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>. This tools often shows up at the top of lists of “what’s your favorite program written in Rust?” when Rust developers are polled. That’s how I discovered it.</p>

<p>It’s extremely fast, has many useful options and has really nice output formatting. I absolutely love it. I probably run <code class="language-plaintext highlighter-rouge">rg -i something</code> in my source tree dozens of times a day. This is one of my must-have tools that I can no longer live without!</p>

<h3 id="building-it-from-source">Building it from source</h3>
<p>Let’s download the source for ripgrep and build it ourselves using the tools we have already installed.</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">git</span><span class="w"> </span><span class="nx">clone</span><span class="w"> </span><span class="nx">https://github.com/BurntSushi/ripgrep</span><span class="w">
</span><span class="n">cd</span><span class="w"> </span><span class="nx">ripgrep</span><span class="w">
</span><span class="n">cargo</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--path</span><span class="w"> </span><span class="o">.</span></code></pre></figure>

<p>By running <code class="language-plaintext highlighter-rouge">cargo install</code> we will both compile the program and install it so it will be available to run from any future terminal windows we open. This is done by installing the tool under ~.cargo\bin, which is in the %path% environment variable.</p>

<p><img src="/images/2019-12-19/rg_build1.png" alt="rg" /></p>

<p>After the build finishes, we can try it out:</p>

<p><img src="/images/2019-12-19/rg1.png" alt="rg" /></p>

<p>Success! We have cloned and built the ripgrep tool from source and installed it so that we can now search files rapidly from any of our terminal session on Windows.</p>

<h3 id="installing-it-with-cargo-in-wsl">Installing it with Cargo in WSL</h3>
<p>Of course we want ripgrep available in our WSL environment as well; while we could once again build it from source, let’s show another way of acquiring programs from the Rust ecosystem using Cargo.</p>

<p><code class="language-plaintext highlighter-rouge">cargo install</code> can be used to build and install programs from <a href="https://crates.io/">crates.io</a>, the Rust package repository. If we don’t want to create our own clone of the source code, this is one of the simplest ways to get the tools we want installed.</p>

<p>From our bash prompt in Ubuntu running on WSL we can simply run:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">cargo <span class="nb">install </span>ripgrep</code></pre></figure>

<p><img src="/images/2019-12-19/rg_install1.png" alt="rg" /></p>

<p>Once again, once the build finishes we have ripgrep installed, this time in our WSL environment. If we open a new bash shell we can try it out:</p>

<p><img src="/images/2019-12-19/rg2.png" alt="rg" /></p>

<h2 id="fastmod">Fastmod</h2>
<p>Another useful tool, similar to ripgrep and also written in Rust, is <a href="https://github.com/facebookincubator/fastmod">fastmod</a>, from Facebook. I use this tool all the time to do code refactorings from the command line. It has a nice feature that it will preview changes to make sure your regex matched correctly.</p>

<p>No mystery for how to get it installed:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">cargo <span class="nb">install </span>fastmod</code></pre></figure>

<p>Here is an example of it in action:</p>

<p><img src="/images/2019-12-19/fastmod1.png" alt="fastmod" /></p>

<h2 id="starship">Starship</h2>
<p>We have one more tool, yet again written in Rust, that we are going to install. This one is used to give us a really nice terminal prompt.</p>

<p>Customizing your terminal prompt is something of an obsessive hobby among command-line aficionados. Just search for ‘bash prompt customization’ on YouTube and you will find an endless stream of videos demonstrating different customizations.</p>

<p>My own current preference is to use a tool called <a href="https://starship.rs/">Starship</a>.</p>

<h3 id="installing-starship-on-windows-using-cargo">Installing Starship on Windows using Cargo</h3>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">cargo</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">starship</span></code></pre></figure>

<p>If you’re impatient there are faster ways to get these tools installed than building them from source with Cargo; Rust, for all of it’s strengths, is not known for fast compilation times. That said, I like building from source because it often finds libraries and packages I may need to install that will be useful when building other projects where I may want to be modifying the source code. It’s also just kind of fun to know you can build from source and go make changes to the programs if you find a need to.</p>

<h3 id="installing-starship-on-ubuntu--wsl-using-cargo">Installing Starship on Ubuntu / WSL using Cargo</h3>
<p>If you try to build Starship from source in the WSL environment we have configured on our new machine so far, it will fail, as there are a few missing dependencies that need to be installed first. However, installing them is straight-forward, so the following should be sufficient to get a really nice looking terminal prompt in bash on WSL:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>apt <span class="nb">install </span>pkg-config
<span class="nb">sudo </span>apt <span class="nb">install </span>libssl-dev
cargo <span class="nb">install </span>starship</code></pre></figure>

<p>Once installed, we will want to add the following to our PowerShell $profile script:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Invoke-Expression</span><span class="w"> </span><span class="p">(</span><span class="o">&amp;</span><span class="n">starship</span><span class="w"> </span><span class="nx">init</span><span class="w"> </span><span class="nx">powershell</span><span class="p">)</span></code></pre></figure>

<p>This will start the Starship prompt when we launch PowerShell.</p>

<p>For Bash, we need to add this to our .bashrc file:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">eval</span> <span class="s2">"</span><span class="si">$(</span>~/.cargo/bin/starship init bash<span class="si">)</span><span class="s2">"</span></code></pre></figure>

<h3 id="configuring-starship">Configuring Starship</h3>
<p>There are a lot of configuration options for Starship, which are <a href="https://starship.rs/config/#prompt">documented here</a>. Customizations go into the file <code class="language-plaintext highlighter-rouge">starship.toml</code> under the ~/.config folder.</p>

<p>I edit the starship.toml file to look like this:</p>

<figure class="highlight"><pre><code class="language-toml" data-lang="toml"><span class="nn">[character]</span>
<span class="py">symbol</span> <span class="p">=</span> <span class="s">"✓"</span>
<span class="py">error_symbol</span> <span class="p">=</span> <span class="s">"✗"</span>
<span class="py">use_symbol_for_status</span> <span class="p">=</span> <span class="kc">true</span>

<span class="nn">[git_status]</span>
<span class="py">disabled</span> <span class="p">=</span> <span class="kc">true</span>

<span class="nn">[time]</span>
<span class="py">disabled</span> <span class="p">=</span> <span class="kc">false</span>
<span class="py">format</span> <span class="p">=</span> <span class="s">"[%T]"</span></code></pre></figure>

<p>I disable git status information because the codebase I work in is too large: <code class="language-plaintext highlighter-rouge">git status</code> is sufficiently slow that it causes a noticeable delay when the prompt re-draws. If your codebase is small enough, you may want to leave the <code class="language-plaintext highlighter-rouge">git_status</code> option enabled.</p>

<p>The end result is a prompt that looks like this:</p>

<p><img src="/images/2019-12-19/starship1.png" alt="starship" /></p>

<h2 id="conclusion">Conclusion</h2>
<p>We’re getting really close to having our new computer running the tools I use every day for my programming work. We’re not finished yet, however. In an upcoming post we will continue our journey and get a few more ‘must-have’ pieces of software installed and configured.</p>]]></content><author><name></name></author><category term="Programming" /><summary type="html"><![CDATA[This is part 4 in my series where I document my process for going from a brand new computer to one that has the basic set of tools I want present to feel productive.]]></summary></entry></feed>