<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Al Rodriguez</title>
    <description>The latest articles on DEV Community by Al Rodriguez (@programmeral).</description>
    <link>https://dev.to/programmeral</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F29296%2Fe224d447-330b-46fb-8e56-03fbd183a9bf.jpeg</url>
      <title>DEV Community: Al Rodriguez</title>
      <link>https://dev.to/programmeral</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/programmeral"/>
    <language>en</language>
    <item>
      <title>New Project: C# Json Serializer Context Registrations Generator</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Mon, 09 Dec 2024 04:20:31 +0000</pubDate>
      <link>https://dev.to/programmeral/new-project-c-json-serializer-context-registrations-generator-bni</link>
      <guid>https://dev.to/programmeral/new-project-c-json-serializer-context-registrations-generator-bni</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post was originally posted at my personal blog at: &lt;a href="https://programmeral.com/posts/20241208_JsonSerializerContextRegistrationGenerator" rel="noopener noreferrer"&gt;https://programmeral.com/posts/20241208_JsonSerializerContextRegistrationGenerator&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New Project: C# Json Serializer Context Registrations Generator
&lt;/h2&gt;

&lt;p&gt;Do you use the built-in System.Text.Json Source Generator to generate code to serialize/deserialize JSON objects without using reflection? If so, you know it can be a hassle to remember to register all of the classes with the code generator. I know I think it's a hassle, and easy to forget.&lt;/p&gt;

&lt;p&gt;That's why I made this project to make the developer experience of registering those classes a little easier. Instead of adding them all above a single class, you can add a custom attribute above each class that should be registered, and then a .NET Tool will generate a class with all of the registrations in the single file for you. Then then built-in System.Text.Json Source Generator will run off that.&lt;/p&gt;

&lt;p&gt;The full details are in the project README at: &lt;a href="https://github.com/ProgrammerAL/json-serializer-context-registration-generator" rel="noopener noreferrer"&gt;https://github.com/ProgrammerAL/json-serializer-context-registration-generator&lt;/a&gt;. It also includes instructions on how to setup your code to use this.&lt;/p&gt;

&lt;h2&gt;
  
  
  NuGet Packages
&lt;/h2&gt;

&lt;p&gt;There are 2 NuGet packages. One for the attributes you add to your code, and the other for a .NET Tool to generate code files based on how the attributes were used. &lt;/p&gt;

&lt;p&gt;The attributes are in package &lt;code&gt;ProgrammerAL.JsonSerializerRegistrationGenerator.Attributes&lt;/code&gt;, and the .NET Tool is at &lt;code&gt;ProgrammerAL.JsonSerializerRegistrationGenerator.Runner&lt;/code&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tooling</category>
    </item>
    <item>
      <title>New Project: Code Updater</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Sun, 03 Nov 2024 19:05:30 +0000</pubDate>
      <link>https://dev.to/programmeral/new-project-code-updater-3mj0</link>
      <guid>https://dev.to/programmeral/new-project-code-updater-3mj0</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20241103_CodeUpdater" rel="noopener noreferrer"&gt;https://programmeral.com/posts/20241103_CodeUpdater&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New Project: Code Updater
&lt;/h2&gt;

&lt;p&gt;Quick, when was the last time you updated all dependencies on all your projects? Not recently enough I bet. The release of .NET 9 is right around the corner. How long will it take you to update your projects to use it? After all, it's just a single character change in the csproj file. In every csproj file. Plus you have to make sure the project still compiles....it can take some time.&lt;/p&gt;

&lt;p&gt;When we get around to updating, there are always manual tasks we do, if we get the time to do it. So why not automate that? I created a .NET Tool to run updates on code in a local directory. The full details are in the project README at: &lt;a href="https://github.com/ProgrammerAL/code-updater" rel="noopener noreferrer"&gt;https://github.com/ProgrammerAL/code-updater&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Overview: What it does
&lt;/h2&gt;

&lt;p&gt;The purpose of this project is to update specific aspects of code within a directory. This is useful when you have a large number of projects that you want to update all at once. This application is assumed to run on a local machine, and then the changes are manually committed to source control. This allows for a more controlled update process, because a developer is meant to manually check the changes before committing them.&lt;/p&gt;

&lt;p&gt;The ReadMe on GitHub (linked above) goes into a lot more details on how to use it. Give it a whirl on a project you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET Tool
&lt;/h2&gt;

&lt;p&gt;The NuGet package is hosted on nuget.org. You can view the NuGet at: &lt;a href="https://www.nuget.org/packages/ProgrammerAL.Tools.CodeUpdater" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/ProgrammerAL.Tools.CodeUpdater&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install the tool locally using: &lt;code&gt;dotnet tool install --global ProgrammerAL.Tools.CodeUpdater --version 1.0.0.47&lt;/code&gt;&lt;/p&gt;

</description>
      <category>blog</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tooling</category>
    </item>
    <item>
      <title>New Project: Public Interface Generator</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Tue, 01 Oct 2024 01:08:34 +0000</pubDate>
      <link>https://dev.to/programmeral/new-project-public-interface-generator-2l29</link>
      <guid>https://dev.to/programmeral/new-project-public-interface-generator-2l29</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20240930_InterfaceSourceGenerator" rel="noopener noreferrer"&gt;https://programmeral.com/posts/20240930_InterfaceSourceGenerator&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New Project: Public Interface Generator
&lt;/h2&gt;

&lt;p&gt;As developers we like to automate things. Anything we can automate is one less thing we waste time on later. Another thing that's getting easier to do, easier than it used to be at least, is to codify our patterns. Enforcing code patterns with code. One common pattern in C# is to create an interface that exists just to make unit test mocking easier. So why not generate the interface code instead of writing it? &lt;/p&gt;

&lt;p&gt;Well that's the project. There's code and a NuGet package and everything. The full details are in the project README at: &lt;a href="https://github.com/ProgrammerAL/public-interface-generator" rel="noopener noreferrer"&gt;https://github.com/ProgrammerAL/public-interface-generator&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Overview: What it does
&lt;/h2&gt;

&lt;p&gt;The purpose of the project is to make a C# Source Generator to create interfaces at compile time. The Source Generator will inspect all classes with the provided [GenerateInterfaceAttribute] attribute and generate an Interface of all public Methods, Properties, and Events.&lt;/p&gt;

&lt;h2&gt;
  
  
  NuGet Package
&lt;/h2&gt;

&lt;p&gt;The NuGet package is hosted on nuget.org. You can get it from: &lt;a href="https://www.nuget.org/packages/PublicInterfaceGenerator" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/PublicInterfaceGenerator&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blog</category>
      <category>sourcegenerator</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Improving Azure Functions Cold Boot</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Tue, 14 May 2024 03:06:33 +0000</pubDate>
      <link>https://dev.to/programmeral/improving-azure-functions-cold-boot-4on8</link>
      <guid>https://dev.to/programmeral/improving-azure-functions-cold-boot-4on8</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20240513_ImprovingAzureFunctionsColdBoot" rel="noopener noreferrer"&gt;https://programmeral.com/posts/20240513_ImprovingAzureFunctionsColdBoot&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why care about cold boot?
&lt;/h2&gt;

&lt;p&gt;A while ago I saw the below tweet from Paul Yuknewicz mentioning that cold start for Azure Functions Apps has improved a lot lately. This is great because cold boot can be been a big pain for HTTP based Function Apps.&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;Cold start in &lt;a href="https://twitter.com/AzureFunctions?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@AzureFunctions&lt;/a&gt; improved ~53% across all regions and languages in last 18 months. Plus we have our own opinion how you can be even more efficient by customizing the concurrent number of events per instance. Learn from perf architect Hamid how this was done and what…&lt;/p&gt;— Paul Yuknewicz (@paulyuki99) &lt;a href="https://twitter.com/paulyuki99/status/1783695782887170297?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;April 26, 2024&lt;/a&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;
  
  
  What is cold boot?
&lt;/h2&gt;

&lt;p&gt;Cold boot is when an application starts up for a request after being shut down for not being in use. With so much of our cloud infrastructure, we live in an on-demand world. That means if something isn't being used, turn it off. The down side to that is that when it turns back on, there's an added delay. And that's cold boot!&lt;/p&gt;

&lt;p&gt;Consumption based Azure Function Apps will shut down after around 5 minutes of non-use. So lets check out how much that affects the app, and what we can do to to improve the start time. Paul's tweet above has a link to a page with tips for improving cold boot time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Base Sample
&lt;/h2&gt;

&lt;p&gt;To start I used the Azure Portal to create the below services. Unless otherwise specified, the default options were used. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure Storage Account

&lt;ul&gt;
&lt;li&gt;V2 storage&lt;/li&gt;
&lt;li&gt;Locally-Redundant Storage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Azure Function App

&lt;ul&gt;
&lt;li&gt;Consumption Plan&lt;/li&gt;
&lt;li&gt;Runtime: .NET 8&lt;/li&gt;
&lt;li&gt;Operating System: Linux&lt;/li&gt;
&lt;li&gt;Storage Account: Link to the one created above&lt;/li&gt;
&lt;li&gt;App Insights: Off&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;After creating the Function App instance I looked at the pre-generated Environment Variables. Those are config settings so the Function App knows where to download the zip package from in order to run the code. Except for &lt;code&gt;WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED&lt;/code&gt; which is a new variable specific to Azure Functions. Microsoft's documentation says it does some kind of optimization. Which is not helpful, but I guess we'll leave it on. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7t8i84hs64vcxltsm0oi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7t8i84hs64vcxltsm0oi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that I opened Visual Studio and created a new Azure Functions project with default values for an HTTP Triggered, .NET 8, Isolated Functions project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing and Testing
&lt;/h2&gt;

&lt;p&gt;Some of these tests will involve manually editing the zip package pushed to Azure. So I chose to create a publish profile in Visual Studio, publishing to my local machine. Before uploading to Azure I move all of the files from my &lt;code&gt;/bin/Release/net8.0/publish&lt;/code&gt; directory to a new zip file.&lt;/p&gt;

&lt;p&gt;When deploying the app using a zip package you must set the &lt;code&gt;WEBSITE_RUN_FROM_PACKAGE&lt;/code&gt; environment variable on the Function App. The value to that environment variable is the URL the Function App runtime will download your app from. For these tests I used a SAS tokento the zip package, until said otherwise.&lt;/p&gt;

&lt;p&gt;As an example, here is the SAS token I used for the &lt;code&gt;WEBSITE_RUN_FROM_PACKAGE&lt;/code&gt; variable: &lt;code&gt;https://alrodrifuncscoldbootexpr.blob.core.windows.net/my-funcs/funcs.zip?sp=r&amp;amp;st=2024-05-08T22:10:20Z&amp;amp;se=2024-05-10T06:10:20Z&amp;amp;spr=https&amp;amp;sv=2022-11-02&amp;amp;sr=b&amp;amp;sig=QfBzOYAwAhYvJR6%2BXEXXM4dKv41al1kbyfWdnm0zxNw%3D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There's no easy way to test cold boot time. I created a small app that will do the below steps. The app was run from my local machine, and the code is in the final section of this post if you want to look it over.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop the Function App&lt;/li&gt;
&lt;li&gt;Wait 10 seconds&lt;/li&gt;
&lt;li&gt;Start the Function App&lt;/li&gt;
&lt;li&gt;Wait 60 seconds&lt;/li&gt;
&lt;li&gt;Loop 10 times

&lt;ul&gt;
&lt;li&gt;Wait 6 minutes&lt;/li&gt;
&lt;li&gt;Send a GET request to an endpoint in the Function App&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I read somewhere once that it takes about 5 minutes for a Function App to be taken down by Azure. So the 6 minute wait is to be sure the Function App has been deallocated in Azure. Plus this will simulate a more realistic cold boot scenario since usually apps cold boot after being deallocated, not from a restart.&lt;/p&gt;

&lt;p&gt;Finally, it's important to remember that we're testing cold boot time of a Function App. What our app does at startup is our own issue, not Azure's. That's why we will be testing with a minimal Function App.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 1: Defaults
&lt;/h2&gt;

&lt;p&gt;The default published version of the app contains 54 filaes and is 5.67 MB, or 2.4 MB when zipped. Deploying the app and testing, we get the below timings. Ranging from 1.3 seconds to 3.2 seconds. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2,250&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2,130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1,901&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1,913&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1,780&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2,448&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1,510&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1,396&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;3,214&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1,823&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Test 2: Delete Unnecessary Files
&lt;/h2&gt;

&lt;p&gt;Part of the cold boot is network activity. The Function App runtime needs to download the zip package, and unzip the files. So the theory is the less bits we have to transfer over the network, the better.&lt;/p&gt;

&lt;p&gt;I've seen a handful of Function App projects that include PDB files in the output. This default app only has 1. The published app also has a &lt;code&gt;runtimes&lt;/code&gt; folder which has some files for Windows and Linux. Since we know this will only run on Linux, I deleted the Windows folder. This removed 5 dlls.&lt;/p&gt;

&lt;p&gt;The new version has 48 files for a total size of 5.67 MB, which is 2.36 MB when zipped. With a savings of 0.04 MB I don't expect much of an improvement, if any.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1,899&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1,855&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1,420&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1,852&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2,451&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1,844&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1,426&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;2,792&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;2,538&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;2,023&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is pretty much the same. Ranging from 1.4 to 2.7 seconds. I guess it averages to being a little better than the previous test, but it's not something a human would notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 3: Ready to Run
&lt;/h2&gt;

&lt;p&gt;Ready to Run is a compiler flag to pre-compile the JIT code for a platform. This saves on startup time because the CLR does not need to compile the JIT code to native code. &lt;/p&gt;

&lt;p&gt;To be clear this is a type of AOT and not the AOT everyone has been talking about recently. Ready to Run has been around in the .NET world for a long time. I don't know the exact reasons why this is different from AOT, because there are similarities, but I know it's different. Also, the sole purpose of Ready to Run is to improve startup time. Anyway, the only change I made to my &lt;code&gt;csproj&lt;/code&gt; file is by adding the below code, then ran publish like normal.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;RuntimeIdentifier&amp;gt;&lt;/span&gt;linux-x64&lt;span class="nt"&gt;&amp;lt;/RuntimeIdentifier&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PublishReadyToRun&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PublishReadyToRun&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the published app moved up to 8.89 MB, which was 3.88 MB when zipped. It also does not have the &lt;code&gt;runtimes&lt;/code&gt; folder, so no need to delete anything from there. There was 1 pdb file, which I elected to not delete because it's small and I forgot to do it before starting the test.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2,041&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2,229&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1,430&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2,486&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2,327&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1,309&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1,703&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1,353&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;2,467&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;2,300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is a little better than the default, but not by much. Ranging from 1.4 to 2.4 seconds. I was hoping for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  AOT
&lt;/h2&gt;

&lt;p&gt;With the release of .NET 8, AOT is all the rage these days. So let's try it by adding the below to the &lt;code&gt;csproj&lt;/code&gt; file, and removing what we added for Ready to Run in the previous test.&lt;/p&gt;

&lt;p&gt;Note: I had to use WSL to compile to Linux.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;RuntimeIdentifier&amp;gt;&lt;/span&gt;linux-x64&lt;span class="nt"&gt;&amp;lt;/RuntimeIdentifier&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PublishAot&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/PublishAot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When published the output is 12 files at 103 MB, which was 32.9 MB when zipped.&lt;/p&gt;

&lt;p&gt;Unfortunatly I was not able to get this to run. I guess AOT isn't supported for .NET 8 Azure Functions on Linux right now? I tried a handful of different ways to get this to run and could not find any documentation online. So I'll ignore it for now. If this is supported in the future I may remember to update this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Flags
&lt;/h2&gt;

&lt;p&gt;There are some other flags you can use in .NET projects to make the final set in files smaller. PublishTrimmed, and SelfContained are the two I see talked about a lot. Unfortunatly, like AOT, they also did not work when I published with those flags on. So no testing there!&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading Functions with a Managed Identity
&lt;/h2&gt;

&lt;p&gt;Quick refresher. When the Function App loads the executables for the Azure Function, it downloads the zip file from a blob in an Azure Storage Account. Up until now, we had the Function App was use a SAS token for the authorization to be allowed to download the file. &lt;/p&gt;

&lt;p&gt;In this test we will be using a Managed Identity on the Function App and granting it the Blob Storage Contributor permissions on the storage account so it can get zip file.&lt;/p&gt;

&lt;p&gt;If you're unfamiliar with Managed Identities I highly reccomend you look into them. I've written about this in the past &lt;a href="https://programmeral.com/posts/20240409-WhyAzureManagedIdentitiesNoMoreSecrets" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but that was from the application level, not for this specific scenario. But the concept is the same.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;14,456&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;11,360&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;20,668&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;15,118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;14,986&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;14,798&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;18,868&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;21,792&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;15,143&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;11,428&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those timings are CRAZY. Over 10x to 15x slower than using a SAS token. I never would have expected this. &lt;/p&gt;

&lt;p&gt;Pure speculation here, I assume the underlying system that gets the auth token is making a call to some other system and that call is what's running slow. But I have no idea if that's true. Again, just guessing. If you know why this is so much slower, please reach out to me on some form of social media (links on the About page!).&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Life Example
&lt;/h2&gt;

&lt;p&gt;I have a personal project that is all grown up and become a full application. Here are some stats about the it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET 8 Isolated&lt;/li&gt;
&lt;li&gt;Uses Managed Identity to load application from zip&lt;/li&gt;
&lt;li&gt;Runs on Linux&lt;/li&gt;
&lt;li&gt;NOT Ready to Run&lt;/li&gt;
&lt;li&gt;76.5 MB unzipped and 26.4 MB zipped&lt;/li&gt;
&lt;li&gt;234 Files

&lt;ul&gt;
&lt;li&gt;7 of those are Windows specific .DLLs&lt;/li&gt;
&lt;li&gt;78 of those are .DLLs (not sure why these are here, likely from a 3rd party Nuget, another thing for me to)&lt;/li&gt;
&lt;li&gt;2 of those are .PDBs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let's go ahead and test it 3 times. The first to get a baseline, the second will remove the Managed Identity, and the third time to test moving the Ready to Run.&lt;/p&gt;

&lt;p&gt;Here are the baseline test results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;17,772&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;15,779&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;11,739&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;15,846&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;11,431&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;21,528&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;15,874&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;15,570&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;16,940&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;16,117&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those numbers are bad. But it's what we expect considering it's still using a Managed Identity to get the zip file. The next test is without the Managed Identity for getting the zip file (it still has a Managed Identity for certain operations).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8,354&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;588&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;9,192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;6,664&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;7,358&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;7,751&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;567&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;7,389&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;9,237&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;7,205&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those numbers are better, but still not great. Two of them are less than 1 second, but considering the rest of the numbers, I feel like it's best to ignore those. The 7-9 second range is half of what we got in the first test. This is an upgrade. &lt;/p&gt;

&lt;p&gt;For the next test we'll add Ready to Run to the project. This updates the project to 228 files at 121 MB, or 48.5 MB when zipped. This means the zip file is twice the size of the previous test, which is similar to what we saw when we added Ready to Run to the default project.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test Id&lt;/th&gt;
&lt;th&gt;Time in ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;6,768&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;6,478&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;6,914&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;9,404&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;8,741&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;8,223&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;6,658&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;7,141&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;6,378&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;7,810&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those numbers are just barely better than before. In my case, it looks like adding Ready to Run didn't improve the cold boot by much. If I were to guess, it's because of the amount of startup code that gets run. There is a lot in this project. It looks like any more gains in this project will be from an code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Code
&lt;/h2&gt;

&lt;p&gt;Below is the code I used to test the cold boot timings. There are some hard coded strings in there, so make sure to change those values if you plan to run this code yourself. I have already deleted the resource group these services were hosted in, so don't think you'll mess with my experiment in some way.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Diagnostics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;StopWaitSeconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;StartWaitSeconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;BetweenTestsWaitSeconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;360&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="c1"&gt;//6 minutes&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;watch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stopArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProcessStartInfo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin\az.cmd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"functionapp stop --name experiment-cold-bool-az-funcs --resource-group experiment-az-funcs-cold-boot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RedirectStandardError&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;startArgs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProcessStartInfo&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin\az.cmd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"functionapp start --name experiment-cold-bool-az-funcs --resource-group experiment-az-funcs-cold-boot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RedirectStandardError&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TestResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Stopping Function App"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stopProcess&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;StartInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stopArgs&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;stopProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;stopProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Waiting &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;StopWaitSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds for stop to complete"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StopWaitSeconds&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting Function App"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;startProcess&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;StartInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;startArgs&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;startProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;startProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Waiting &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;StartWaitSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds to start to complete"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StartWaitSeconds&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;runId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Testing Run: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;runId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;//I've been told Azure Functions a brought down after 5 minutes&lt;/span&gt;
    &lt;span class="c1"&gt;//  Wait 6 minutes to be sure it's down&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Waiting &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BetweenTestsWaitSeconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds for app to be shut down"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BetweenTestsWaitSeconds&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://experiment-cold-bool-az-funcs.azurewebsites.net/api/Function1?"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;httpResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;httpResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Exception result from endpoint with status code &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;httpResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Completed test "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TestResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Elapsed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;DisplayResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Completed with timings:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"| Test Id | Time in ms |"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"| :---------: | :-------: |"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;timing&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;DisplayResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DisplayResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"| &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; |"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;TestResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TestNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>blog</category>
      <category>azurefunctions</category>
      <category>serverless</category>
      <category>performance</category>
    </item>
    <item>
      <title>New Project: SVGHelpers.com</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Mon, 06 May 2024 04:22:08 +0000</pubDate>
      <link>https://dev.to/programmeral/new-project-svghelperscom-5424</link>
      <guid>https://dev.to/programmeral/new-project-svghelperscom-5424</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20240506_SvgHelpers"&gt;https://programmeral.com/posts/20240506_SvgHelpers&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New Project: SvgHelpers.com
&lt;/h2&gt;

&lt;p&gt;Statistically, you aren't aware I make programming themed &lt;a href="https://programmeral.com/comics/latest"&gt;web comics&lt;/a&gt;. Generic and stupid jokes, kind of like the Dilbert guy, but I'm way less problematic and don't own a pool shaped like Dilbert's head (look it up (both of them)). Anyway, I needed some tools to help create those image files, so I created SVG Helpers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's an SVG?
&lt;/h2&gt;

&lt;p&gt;If you're not familiar, the SVG image format is an XML syntax of primitive shapes. So instead of other formats like PNG, JPEG, etc that specify each individual pixel, SVGs are instructions on what to draw at runtime. Think something similar to HTML, but less abstract. The example below is an SVG image which draws a rectangle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"&amp;gt;
    &amp;lt;rect x="5" y="5" heigh="10" width="20" fill="green"&amp;gt;&amp;lt;/rect&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like them because I have no drawing or make-things-look-pretty skills (exhibit 1: this website), and they scale with no extra work.&lt;/p&gt;

&lt;p&gt;The cool thing is they also let you embed custom code directly into the file, like CSS or Javascript. Because of that feature, the web comics I've created so far have support for dark mode. BUt this feature can be a double edged sword. Because loading unknown code is a big security issue, a lot of sites don't let you upload SVGs. So there are extra hoops you have to jump through to upload to other sites. But for your own site, it doesn't matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  SvgHelpers.com
&lt;/h2&gt;

&lt;p&gt;I create my SVGs artisanally by hand, so some new tools are needed to help with the creation process. And thus &lt;a href="//SvgHelpers.com"&gt;SvgHelpers.com&lt;/a&gt; was born. The purpose of the site is to host utilities that make hand editing SVG files easier. Right now there are only 2 features, the SVG Mover and Continuous Refresher.&lt;/p&gt;

&lt;h3&gt;
  
  
  SVG Mover
&lt;/h3&gt;

&lt;p&gt;The first feature moves all elements in the given XML based on X and Y inputs. It's really common for me to create a section of a comic and later decide to make one section biger or smaller, requiring other elements to be moved. This makes it easy to copy/paste in the section that needs to move, and automate moving those elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Refresher
&lt;/h3&gt;

&lt;p&gt;The second feature will allow you to "upload" a file to the web client. It will then display the image on screen and refresh from the file every second. When editing SVGs, I usually have the code up in Visual Studio Code and then have to manually refresh a web browser on a different monitor to see changes. This tool saves me the couple seconds switching over to the browser and hitting refresh. This will save a load of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use any of the many tools that already exist?
&lt;/h2&gt;

&lt;p&gt;I didn't feel like it. Honestly I don't know what's out there and my current process for editing SVGs works well enough. In the future I might change things up, but for now this does what I need. &lt;/p&gt;

&lt;h2&gt;
  
  
  Requesting Features
&lt;/h2&gt;

&lt;p&gt;The code is open source on GitHub at &lt;a href="https://github.com/ProgrammerAL/SvgHelpers"&gt;https://github.com/ProgrammerAL/SvgHelpers&lt;/a&gt;. If you think you have a good idea on what to add, open an issue. I also accept PRs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Behind the Site
&lt;/h2&gt;

&lt;p&gt;This is a C# Blazor WebAssembly site. All hosted in the browser with no server side components. The static files are hosted on Cloudflare Pages, and everything is deployed with IaC using Pulumi run through GitHub Actions. &lt;/p&gt;

&lt;p&gt;If you're any more curious, you can look at the source code at &lt;a href="https://github.com/ProgrammerAL/SvgHelpers"&gt;https://github.com/ProgrammerAL/SvgHelpers&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>svg</category>
    </item>
    <item>
      <title>Shift Left</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Sat, 20 Apr 2024 18:36:07 +0000</pubDate>
      <link>https://dev.to/programmeral/shift-left-46h0</link>
      <guid>https://dev.to/programmeral/shift-left-46h0</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20240420_ShiftLeftComic"&gt;https://programmeral.com/posts/20240420_ShiftLeftComic&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dpp52h1g1cmp8w5gyx2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dpp52h1g1cmp8w5gyx2.png" alt="Shift Left" width="640" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On Shifting Left
&lt;/h2&gt;

&lt;p&gt;I really, really hope no one takes this as an insult. I think (no, I know!) everyone working in the software pipeline does a ton of work. But when I hear Shift Left I hear a lot of people putting work on someone else's plate. &lt;/p&gt;

&lt;p&gt;This isn't a complaint. Shifting Left is a good thing. We do things earlier in the process, where it's cheaper and easier to accomplish. It's a win for everyone. But with more and more tasks being shifted earlier in the pipeline, at some point it feels like we all just have more work to do.&lt;/p&gt;

&lt;p&gt;Anyway, I hope you enjoyed this inaugural comic.&lt;/p&gt;

</description>
      <category>comic</category>
      <category>humor</category>
    </item>
    <item>
      <title>Azure Managed Identities: No more secrets</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Wed, 10 Apr 2024 02:55:11 +0000</pubDate>
      <link>https://dev.to/programmeral/azure-managed-identities-no-more-secrets-19cc</link>
      <guid>https://dev.to/programmeral/azure-managed-identities-no-more-secrets-19cc</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20240409-WhyAzureManagedIdentitiesNoMoreSecrets"&gt;https://programmeral.com/posts/20240409-WhyAzureManagedIdentitiesNoMoreSecrets&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets are a hassle
&lt;/h2&gt;

&lt;p&gt;It's very easy for our applications to be littered with secrets. Connection strings, access tokens, passwords, etc. We can't escape them, but we can avoid them where possible. Tracking and managing application secrets is a chore no one likes to do and is so easy to mess up.&lt;/p&gt;

&lt;p&gt;In this post we'll talk about Azure Managed Identities. A feature that lets applications hosted in Azure interact with other resources in Azure without using a plain text secret. You can think of it kind of like a Service Account. It has permissions assigned to it, and it grants those permissions to whichever apps are using it. The big difference is that an Azure Managed Identity doesn't need a password. It's all taken care of by Azure, hence the word "Managed".&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Azure Managed Identities are an identity your Azure hosted app can use with RBAC permissions. Create an identity, assign permissions to it, and assign the identity to an application hosted in Azure. Then make a few code changes so your app uses the identity. Now you don't need a connection string to interact with Azure resources like Key Vault, Storage Accounts, etc.&lt;/p&gt;

&lt;p&gt;Microsoft has a ton of documentation at: &lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview"&gt;&lt;/a&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview"&gt;https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Service Principals?
&lt;/h2&gt;

&lt;p&gt;You may have heard of Service Principals before. If you haven't, feel free to skip this section, it's not that important. A Managed Identity is a type of Service Principal. It's an abstraction to simplify using them. With a Service Principal you are managing the client secrets. A person has to track how long until the client secret expires, and before it expires that person has to cycle it and update all apps that use it. All of that is manual work that Managed Identities automate for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now that I know all that, why should I use a Managed Identity?
&lt;/h2&gt;

&lt;p&gt;So you don't have to manage the secrets, so you don't have to rotate them, so you don't have to worry someone has it when they shouldn't. You don't have secrets in your codebase, chat history, emails, etc. You can't leak secrets you don't have.&lt;/p&gt;

&lt;p&gt;So many of us, myself included, have worked somewhere that lets connection strings or access tokens leak to anyone within the company. Once a secret leaks (and it will), you have to cycle it to a new value, track down evey app that uses it, and update it to use the new value.&lt;/p&gt;

&lt;p&gt;A Managed Identity still uses a token internally. But we're abstracted away from it so we don't even see it. And even better, the token is short lived. &lt;/p&gt;

&lt;p&gt;You can't get rid of all secrets, but you can limit how many are used.&lt;/p&gt;

&lt;h2&gt;
  
  
  How are Managed Identities created?
&lt;/h2&gt;

&lt;p&gt;You have 2 options for creating them, called User Assigned and System Assigned identities. User Assigned identities are just another resource in Azure. You can go into the Azure Portal and create a new Managed Identity resource. From there you can assign permissions to it, and then let as many applications use it.&lt;/p&gt;

&lt;p&gt;The other option is a System Assigned identity. This is created on the application resource that will use it (Function App, Container App, App Service, etc) and can only be used by that application. To enable the System Assigned identity, go to the existing resource in the Azure Portal and click on the Identity option in the left-hand menu. On the default tab 'System assigned' selected, switch the Status toggle to On, then click save. A new Managed Identity will be created that is only used by this resource. You can then assign permissions to it just like any other identity.&lt;/p&gt;

&lt;p&gt;Which one should you use? A User Assigned or a System Assigned identity? I guess it depends on how you manage them. My default choice is a System Assigned identity because it can only be assigned to one service. It also goes well with IaC like Pulumi. On the other hand, User Assigned identities feel more like groups. You assign permissions to one, and every app using it gets that set of permissions. But you have to make sure you don't add more permissions than are needed, and also need to remember to delete the identity when it's no longer in use. In my mind, User Assigned identities feel like more work.&lt;/p&gt;

&lt;h2&gt;
  
  
  You've convinced me. How do I used a Managed Identity?
&lt;/h2&gt;

&lt;p&gt;Thankfully that's pretty easy. Below are two C# code samples creating an instance of &lt;code&gt;BlobServiceClient&lt;/code&gt;. I've only done this with C#, but I'm told the concept is the same across other languages used in Azure apps like JavaScript/TypeScript, Python, Java, etc. The first sample uses a connection string to connect to a blob service and the second does the same thing using the &lt;code&gt;DefaultAzureCredential&lt;/code&gt; class for authentication. When your app runs, the instance of &lt;code&gt;DefaultAzureCredential&lt;/code&gt; checks a couple of places for credentials. It checks for environment variables, managed identities, local sign-ins of Azure PowerShell or the Azure CLI.&lt;/p&gt;

&lt;p&gt;This means when the app runs in Azure, &lt;code&gt;DefaultAzureCredential&lt;/code&gt; will find the Managed Identity assigned to it. And when the app runs on a developer machine, it will use your developer credentials. Just lie before, your code doesn't change across environments, but now you don't have to have a token stored locally, or shared across the dev team.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"DefaultEndpointsProtocol=https;AccountName=MYSTORAGEACCOUNT;AccountKey=Eby8vdM02xNOcqFlqUwJPLl...;EndpointSuffix=core.windows.net"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BlobServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;accountUri&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://MYSTORAGEACCOUNT.blob.core.windows.net/my-container"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BlobServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: What about Azure Managed SQL?
&lt;/h2&gt;

&lt;p&gt;It works there too! But differently. You have to enable a feature on the SQL Server to connect to Entra, and then run some queries to let the database know what database permissions to give to the identity. The concept is the same, but the steps are different. You can read the tutorial with Microsoft's documentation here: &lt;a href="https://learn.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database"&gt;&lt;/a&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database"&gt;https://learn.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>identity</category>
      <category>security</category>
    </item>
    <item>
      <title>Why developers should like OpenTelemetry</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Tue, 05 Mar 2024 02:07:41 +0000</pubDate>
      <link>https://dev.to/programmeral/why-developers-should-like-opentelemetry-1co</link>
      <guid>https://dev.to/programmeral/why-developers-should-like-opentelemetry-1co</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted at my personal site at: &lt;a href="https://programmeral.com/posts/20240304-WhyDevsShouldLikeOTel"&gt;https://programmeral.com/posts/20240304-WhyDevsShouldLikeOTel&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Developers Should Like OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;I often see people looking at OpenTelemetry (OTel) and thinking it's not very useful because other tools they use already do the same thing. So why switch? For me, the short answer is because it's a single standard, but more on that below.&lt;/p&gt;

&lt;p&gt;Before we get to the full answer, let's do a quick history review to make sure we're on the same page.&lt;/p&gt;

&lt;p&gt;By the way, I will not be recapping what OTel is. If you're unfamiliar with that awesome tool, ask an A.I. chatbot or something. Then double check what it tells you by clicking on at least one of those reference sites, because those things are still way too wrong to trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability
&lt;/h2&gt;

&lt;p&gt;For a long time the only way to observe what an application was doing was to look at log statements. So as developers, that's all we did; we just pushed log messages. Sometimes a, "Starting process ABC" message or simply outputting a stack trace was all we needed. When something went wrong we could trace the flow of the process with those logs.&lt;/p&gt;

&lt;p&gt;Eventually I.T. departments started adding their own information to track how the app is running. Things like CPU/Memory/Disk usage, average request length, requests per minute, etc. But that info was stored in different places from the logs, so combining logs and these app metrics wasn't easy. &lt;/p&gt;

&lt;p&gt;Once all this info became commonplace, the business side started using it all to answer questions. How many carts are abandoned? Are responses returned too slow that people leave the site? And more (I assume!).&lt;/p&gt;

&lt;p&gt;For a long time all this info used to answer business questions was stored in separate systems. Developers would put information in log statements, which would have to be queried with some kind of string searching. Other information would come from a database or some external third party. Combining this data was a hassle, and a big part of someone's job (thankfully not mine).&lt;/p&gt;

&lt;p&gt;Fast forward to today and just about every logging provider out there has an SDK to let you send all of this information to them. Developers can send application logs to the provider, while the infrastructure team can send server metrics to that same external service. Some of these providers use an agent you run on the same machine, others are a code change that require a package reference. And however you use that provider, the code is different for each. &lt;/p&gt;

&lt;h2&gt;
  
  
  Just answer the question. Why should devs like OTel?
&lt;/h2&gt;

&lt;p&gt;Finally, with the history lesson out of the way, lets answer the question, "Why should developers like OTel?" I said above the short answer is because it's a single standard. Whenever a phrase like that pops up, a single standard, everyone says the big benefit is that you can move your data from one provider to another. That's cool, but, when does a company do that? Every few years at most? The longer answer is that it's a single API that you (a developer!) have to learn.&lt;/p&gt;

&lt;p&gt;Yeah, it's not difficult to learn a logging API. But they each have different feature sets with different pros/cons. Different types of data you can send. Different dashboards that query the data in different ways. It's a whole project to get one setup effectively. With OTel you have a single API you can learn in and out, and know you can use it across all of your applications. You can also change companies and/or tech stacks and know you can push out the same data without learning a whole new system first. &lt;/p&gt;

&lt;p&gt;With OTel, you can develop locally and push traces/spans/metrics to a local listener to view. To be fair this can be a bit of a hassle to setup, and it's not as easy as just seeing a log message, but things are getting easier. Plus you have companies like Microsoft building things like Aspire to help the dev loop (if you haven't heard of Aspire before, it's really cool and you should &lt;a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview"&gt;check it out&lt;/a&gt;). After you commit your changes and push to qa/staging/prod/etc environments, the code can push to an external provider like Azure Application Insights, Honeycomb, etc. A ton of companies have support for OTel, and your provider is probably on the list. It's the same data in each environment, including your local environment.&lt;/p&gt;

&lt;p&gt;And that's it. OTel is a time saver for developers that will let us focus on writing code for the application and worry less about a requirement every app needs.&lt;/p&gt;

</description>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>Announcing a new podcast: Developer Side Quests</title>
      <dc:creator>Al Rodriguez</dc:creator>
      <pubDate>Sun, 28 Apr 2019 20:03:42 +0000</pubDate>
      <link>https://dev.to/programmeral/announcing-a-new-podcast-developer-side-quests-266b</link>
      <guid>https://dev.to/programmeral/announcing-a-new-podcast-developer-side-quests-266b</guid>
      <description>&lt;p&gt;Something we've all done at one point or...hundreds, is creating a side project to learn something, play with new technologies, or to build something we just want to exist. The point of this new show is to talk about what we do to level up our development skillset outside of our day jobs. So that's where this new podcast comes in, to highlight those stories. I hope you'll give it a listen and tell your friends and other player characters out there.&lt;/p&gt;

&lt;p&gt;In addition to that, I'm always looking for guests. If you have a side project you're just starting, half-way through, previously finished, or only thinking about, I would love to have you on the show. You can send a direct message on Twitter to @DevSideQuests, or an email to &lt;a href="mailto:DeveloperSideQuests@outlook.com"&gt;DeveloperSideQuests@outlook.com&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Don't feel self-conscious if you think no one will care about what you've done. Everyone levels up from somewhere. And everyone learns differently. So your perspective is unique to you and I want to tell other people about it.&lt;/p&gt;

&lt;p&gt;I'm still trying to get all this pushed out to all podcast apps, but the big ones are already there with links on the site, DeveloperSideQuestsPodcast.com.&lt;/p&gt;

</description>
      <category>podcast</category>
    </item>
  </channel>
</rss>
