<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Ed Andersen: Ted's Tech]]></title><description><![CDATA[Roughly weekly newsletter with analysis of news for software developers]]></description><link>https://www.edandersen.com/s/teds-tech-newsletter</link><image><url>https://substackcdn.com/image/fetch/$s_!8VmJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c2f46f2-1699-40b5-9851-b9670a4403d7_330x330.png</url><title>Ed Andersen: Ted&apos;s Tech</title><link>https://www.edandersen.com/s/teds-tech-newsletter</link></image><generator>Substack</generator><lastBuildDate>Mon, 05 Oct 2026 19:02:51 GMT</lastBuildDate><atom:link href="https://www.edandersen.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Ed Andersen]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[edandersen@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[edandersen@substack.com]]></itunes:email><itunes:name><![CDATA[Ed Andersen]]></itunes:name></itunes:owner><itunes:author><![CDATA[Ed Andersen]]></itunes:author><googleplay:owner><![CDATA[edandersen@substack.com]]></googleplay:owner><googleplay:email><![CDATA[edandersen@substack.com]]></googleplay:email><googleplay:author><![CDATA[Ed Andersen]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The Best 11 New Features in .NET 11 and C# 15]]></title><description><![CDATA[What we all look forward to every year]]></description><link>https://www.edandersen.com/p/the-best-11-new-features-in-net-11</link><guid isPermaLink="false">https://www.edandersen.com/p/the-best-11-new-features-in-net-11</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Sat, 26 Sep 2026 04:42:56 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/eeb7e077-674a-4261-b849-3cb6ac5a390f_3840x2160.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>November 2026 will see the release of .NET 11, the next STS release of .NET, which comes with C# 15. Let me go through the best 11 new features, and if you&#8217;d prefer this post in cringe YouTube video form, here it is below:</p><div id="youtube2-Qxy8-cKE5pQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;Qxy8-cKE5pQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/Qxy8-cKE5pQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><p>.NET 11 includes changes to C# syntax (some would say going a bit too far), ASP.NET Core request handling, and the machinery behind async methods. The eleven examples below show how those changes can change and improve code written for .NET 10.</p><p>I&#8217;ve even created a <a href="https://github.com/edandersen/dotnet-11-whats-new">lovely sample project on GitHub</a> that has a `net10-old` and a `net11-new` directory for each feature. The older project shows the approach available in .NET 10; the newer one demonstrates the .NET 11addition. You can directly run the samples below from the GitHub solution!</p><h2><strong>C# 15</strong></h2><h3><strong>1. Collection expression arguments</strong></h3><p>In the first example we use our list of YouTube subscribers as a demo.</p><p>A collection expression can now start with <code>with(...)</code> to pass arguments to the underlying constructor or factory. Capacity and comparer choices can sit inside the initializer. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions#collection-expression-arguments">C# collection expression documentation</a>.</p><p>Here the list reserves room for twice the initial subscriber count. The set treats differently capitalized greetings as equal. In .NET 10, constructing the list and adding its contents are separate operations.</p><h4><strong>.NET 10</strong></h4><pre><code><code>string[] names = ["Gary", "Barry", "Nigel"]; 

List&lt;string&gt; subscribers = new(capacity: names.Length * 2);
subscribers.AddRange(names);

Console.WriteLine($"YouTube subscribers: {string.Join(", ", subscribers)}; capacity: {subscribers.Capacity}");
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>string[] names = ["Gary", "Barry", "Nigel"];

List&lt;string&gt; subscribers = [with(capacity: names.Length * 2), .. names];

Console.WriteLine($"YouTube subscribers: {string.Join(", ", subscribers)}; capacity: {subscribers.Capacity}");
</code></code></pre><p>The C# 15 version supplies the list capacity and set comparer through <code>with(...)</code>. Both programs print:</p><pre><code><code>YouTube subscribers: Gary, Barry, Nigel; capacity: 6</code></code></pre><p>C# is starting to look like another programming language and whilst useful I think this new stuff needs to be toned down. Being able to combine those initial values with constructor arguments is useful but let&#8217;s stop yeah.</p><h3><strong>2. Union types</strong></h3><p>Finally, union types - which we had to wait for AI to &#8220;end coding for good&#8221; to actually get. Previously switch statements would get warnings without fallback default arms as the types couldn&#8217;t be exhaustive.</p><p>C# 15 can declare the case set explicitly with <code>public union Result(Success, Failure);</code>. Case types convert implicitly into the union, and switch expressions receive exhaustiveness checking. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union">C# union type documentation</a>.</p><h4><strong>.NET 10</strong></h4><pre><code><code>Result result = new Success("Thanks for subscribing on YouTube!");
Console.WriteLine(Describe(result));

static string Describe(Result result) =&gt; result switch
{
    Success(var message) =&gt; message,
    Failure(var code) =&gt; $"Error {code}",
    _ =&gt; throw new ArgumentOutOfRangeException(nameof(result))
};

// C# 14: an inheritance hierarchy approximates a fixed set of cases.
// The compiler cannot prove these are all possible subclasses.
public abstract record Result;
public sealed record Success(string Message) : Result;
public sealed record Failure(int ErrorCode) : Result;
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>Result result = new Success("Thanks for subscribing on YouTube!");
Console.WriteLine(Describe(result));

static string Describe(Result result) =&gt; result switch
{
    Success(var message) =&gt; message,
    Failure(var code) =&gt; $"Error {code}"
};

public record Success(string Message);
public record Failure(int ErrorCode);

// C# 15: a value can be exactly one of the declared case types.
public union Result(Success, Failure);
</code></code></pre><p>Both programs print <code>Thanks for subscribing on YouTube!</code>. The .NET 11 switch covers the two declared case types without a fallback. Remove either arm and the compiler warns that the switch is incomplete.</p><p>The two records in the union version don&#8217;t inherit from a common result class. The union itself defines their relationship. This is useful when an API needs to return one of several outcomes without forcing those outcomes into an inheritance hierarchy.</p><h3><strong>3. Closed class hierarchies</strong></h3><p>A similar example next - we cannot be certain of all types that could inherit from a class without a closed hierarchy.</p><p>When the cases already share a base class, <code>closed</code> makes the set of direct descendants known to the compiler. A closed class is implicitly abstract, and only its declaring assembly can add direct descendants. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/closed">The closed modifier reference</a>.</p><p>This gate has a closed state and an open state carrying a percentage. The .NET 10 switch needs a fallback because an ordinary abstract base record doesn&#8217;t restrict its direct descendants to this assembly.</p><h4><strong>.NET 10</strong></h4><pre><code><code>GateState state = new Open(75);
Console.WriteLine(Describe(state));

static string Describe(GateState state) =&gt; state switch
{
    Closed =&gt; "closed",
    Open(var percent) =&gt; $"{percent}% open",
    _ =&gt; throw new ArgumentOutOfRangeException(nameof(state))
};

// C# 14: direct subclasses cannot be restricted to this assembly.
public abstract record class GateState;
public sealed record class Closed : GateState;
public sealed record class Open(float Percent) : GateState;
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>GateState state = new Open(75);
Console.WriteLine(Describe(state));

static string Describe(GateState state) =&gt; state switch
{
    Closed =&gt; "closed",
    Open(var percent) =&gt; $"{percent}% open"
};

// C# 15: only this assembly can declare direct descendants of GateState.
public closed record class GateState;
public sealed record class Closed : GateState;
public sealed record class Open(float Percent) : GateState;
</code></code></pre><p>Both versions print <code>75% open</code>. Marking <code>GateState</code> as <code>closed</code> lets the compiler check that the switch handles both direct descendants.</p><p>The restriction applies to direct inheritance. A descendant can still allow further inheritance unless it is also sealed or closed. Here both descendants are sealed, which keeps the whole hierarchy fixed.</p><h3><strong>4. Extension indexers</strong></h3><p>Something for the enterprise archtiect to add to helpers.dll next. I&#8217;d probably just write ElementAt but if you wanted to reduce this further in your program only, you could already in C# 14 add an &#8220;At&#8221; extention method.</p><p>C# 15 goes further and allows an extension block to declare an indexer on its receiver. That gives calling code bracket syntax for a type that doesn&#8217;t declare that indexer itself. The block needs a named receiver because the indexer operates on an instance. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extension#extension-indexers">Extension member documentation</a>.</p><h4><strong>.NET 10</strong></h4><pre><code><code>IEnumerable&lt;int&gt; numbers = Enumerable.Range(1, 4);
Console.WriteLine($"Third: {numbers.At(2)}");

public static class SequenceExtensions
{
    public static int At(this IEnumerable&lt;int&gt; sequence, int index)
        =&gt; sequence.ElementAt(index);
}
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>IEnumerable&lt;int&gt; numbers = Enumerable.Range(1, 4);
Console.WriteLine($"Third: {numbers[2]}");

public static class SequenceIndexer
{
    extension(IEnumerable&lt;int&gt; sequence)
    {
        public int this[int index] =&gt; sequence.ElementAt(index);
    }
}
</code></code></pre><p>Both programs print <code>Third: 3</code>. The call changes from <code>numbers.At(2)</code> to <code>numbers[2]</code>.</p><h3><strong>5. Labeled break and continue</strong></h3><p>The little booleans used only to escape an outer loop are syntactic noise I&#8217;d happily throw in the bin.</p><p>A plain <code>break</code> inside a nested loop exits the inner loop. If an event should stop the outer loop too, older code often stores a flag and checks it after the inner loop finishes.</p><p>C# 15 lets <code>break</code> and <code>continue</code> name their enclosing loop. A labeled <code>break</code> can also target an enclosing <code>switch</code>. Place the label directly on the construct it identifies. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/jump-statements#labeled-break-and-continue-statements">C# jump statement reference</a>.</p><p>In this itinerary, a closed bridge skips the rest of the current day. Finding the goal ends the entire trip.</p><p>I&#8217;ve also called the label &#8220;barry&#8221; for funsies.</p><h4><strong>.NET 10</strong></h4><pre><code><code>string[][] dailyRoutes =
[
    ["North", "West"],
    ["South", "Bridge closed", "East"],
    ["Central", "Goal", "Harbor"],
    ["Unused"]
];

bool stopAllDays = false;
for (int day = 0; day &lt; dailyRoutes.Length; day++)
{
    Console.WriteLine($"Day {day + 1}:");
    bool skipRestOfDay = false;

    foreach (string route in dailyRoutes[day])
    {
        if (route == "Bridge closed")
        {
            Console.WriteLine("  Bridge closed; next day.");
            skipRestOfDay = true;
            break;
        }

        if (route == "Goal")
        {
            Console.WriteLine("  Goal found; stop all days.");
            stopAllDays = true;
            break;
        }

        Console.WriteLine($"  Visit {route}.");
    }

    if (stopAllDays)
        break;

    if (skipRestOfDay)
        continue;

    Console.WriteLine("  Day completed.");
}
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>string[][] dailyRoutes =
[
    ["North", "West"],
    ["South", "Bridge closed", "East"],
    ["Central", "Goal", "Harbor"],
    ["Unused"]
];

barry: for (int day = 0; day &lt; dailyRoutes.Length; day++)
{
    Console.WriteLine($"Day {day + 1}:");

    foreach (string route in dailyRoutes[day])
    {
        if (route == "Bridge closed")
        {
            Console.WriteLine("  Bridge closed; next day.");
            continue barry;
        }

        if (route == "Goal")
        {
            Console.WriteLine("  Goal found; stop all days.");
            break barry;
        }

        Console.WriteLine($"  Visit {route}.");
    }

    Console.WriteLine("  Day completed.");
}
</code></code></pre><p>The .NET 10 version needs <code>skipRestOfDay</code> and <code>stopAllDays</code>. The C# 15 version names the outer loop <code>barry</code> and jumps directly to that loop with <code>continue barry;</code> or <code>break barry;</code>.</p><p>Both programs produce:</p><pre><code><code>Day 1:
  Visit North.
  Visit West.
  Day completed.
Day 2:
  Visit South.
  Bridge closed; next day.
Day 3:
  Visit Central.
  Goal found; stop all days.
</code></code></pre><p>The jump states which loop it controls, so the outer-loop flag checks disappear. Bargain.</p><h3><strong>6. Extended layout support</strong></h3><p>We&#8217;re deep in native interop territory in this one for gigabrains. If you&#8217;re matching a C-style union in a native library, repeating a field offset for every member adds noise. Declaring the union layout lets the runtime handle that shared-storage arrangement.</p><p>Native interop sometimes requires two fields to occupy the same bytes. The older explicit-layout approach specifies an offset for every field. C# 15 recognizes <code>[ExtendedLayout]</code> and emits metadata that the .NET 11 runtime uses to lay out the type. <a href="https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview1/csharp.md#extended-layout-support">Microsoft&#8217;s extended layout release note</a>.</p><p>Selecting <code>ExtendedLayoutKind.CUnion</code> gives the following integer and float fields shared storage.</p><h4><strong>.NET 10</strong></h4><pre><code><code>using System.Reflection;
using System.Runtime.InteropServices;

// The old explicit-layout approach requires an offset for every union field.
IntFloatBits bits = new() { AsInt = 0x3F800000 };
Console.WriteLine($"Integer bits: 0x{bits.AsInt:X8}");
Console.WriteLine($"Same bits as float: {bits.AsFloat:F1}");
Console.WriteLine($"Metadata layout: {typeof(IntFloatBits).Attributes &amp; TypeAttributes.LayoutMask}");

[StructLayout(LayoutKind.Explicit)]
public struct IntFloatBits
{
    [FieldOffset(0)] public int AsInt;
    [FieldOffset(0)] public float AsFloat;
}
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>using System.Reflection;
using System.Runtime.InteropServices;

// C# 15 emits extended-layout metadata from the new .NET 11 attribute.
IntFloatBits bits = new() { AsInt = 0x3F800000 };
Console.WriteLine($"Integer bits: 0x{bits.AsInt:X8}");
Console.WriteLine($"Same bits as float: {bits.AsFloat:F1}");
Console.WriteLine($"Metadata layout: {typeof(IntFloatBits).Attributes &amp; TypeAttributes.LayoutMask}");

[ExtendedLayout(ExtendedLayoutKind.CUnion)]
public struct IntFloatBits
{
    public int AsInt;
    public float AsFloat;
}
</code></code></pre><p>Both programs write the integer bit pattern <code>0x3F800000</code> and read the same bits through <code>AsFloat</code> as <code>1.0</code>. The final line reports <code>ExplicitLayout</code> in .NET 10 and <code>ExtendedLayout</code> in .NET 11.</p><p>This is an interop representation where code must know which field contains a valid value. The type-safe union in feature 2 models a choice between cases; this C-style union overlays raw storage. Use the layout that matches the native ABI you need to call.</p><h2><strong>ASP.NET Core 11</strong></h2><h3><strong>7. ShortCircuit on MVC controllers and actions</strong></h3><p>MVC is still my favorite and I&#8217;m over the moon Microsoft are still improving it. </p><p>A controller serving a dynamic robots.txt is a useful reason to keep the routing behavior beside the action. The response below is constant so we can concentrate on which middleware runs.</p><p>ASP.NET Core already supports the <code>.ShortCircuit()</code> mapping convention. .NET 11 adds <code>[ShortCircuit]</code>, allowing an MVC controller or action to declare the behavior beside its route metadata. Routing executes the endpoint immediately and skips middleware later in the pipeline. <a href="https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-11#short-circuit-endpoints-with-an-attribute">ASP.NET Core short-circuit release notes</a>.</p><p>The endpoint below serves <code>robots.txt</code>. Middleware after <code>UseRouting</code> adds a diagnostic header when it runs. The .NET 10 version applies the convention while mapping a controller route; the .NET 11 version places the attribute on the controller.</p><h4><strong>.NET 10</strong></h4><pre><code><code>using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

var app = builder.Build();
app.UseRouting();
app.Use(async (context, next) =&gt;
{
    context.Response.Headers["X-Later-Middleware-Ran"] = "yes";
    await next(context);
});

// In .NET 10 the short-circuit convention must be attached when mapping
// endpoints. Conventional routing is used to target this MVC action.
app.MapControllerRoute(
    name: "robots",
    pattern: "robots.txt",
    defaults: new { controller = "Robots", action = "Get" })
    .ShortCircuit();

app.Run();

public sealed class RobotsController : ControllerBase
{
    public IActionResult Get() =&gt; Content("User-agent: *\nDisallow:", "text/plain");
}
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

var app = builder.Build();
app.UseRouting();
app.Use(async (context, next) =&gt;
{
    context.Response.Headers["X-Later-Middleware-Ran"] = "yes";
    await next(context);
});

app.MapControllers();
app.Run();

[ApiController]
[Route("robots.txt")]
[ShortCircuit]
public sealed class RobotsController : ControllerBase
{
    [HttpGet]
    public IActionResult Get() =&gt; Content("User-agent: *\nDisallow:", "text/plain");
}
</code></code></pre><p>With both web apps running, inspect the responses:</p><pre><code><code>curl.exe -i http://localhost:5010/robots.txt
curl.exe -i http://localhost:5011/robots.txt
</code></code></pre><p>Both return <code>200</code> with the robots text. Neither response contains <code>X-Later-Middleware-Ran</code>, because routing executed the endpoint before that middleware.</p><p>Only apply short-circuiting when the endpoint can produce its response without later middleware running. If you need Authentication and CORS this can be screwed up big time so be careful.</p><h3><strong>8. Async validation for Minimal APIs</strong></h3><p>The Minimal API crowd gets something too. An email lookup can swallow a small registration handler once you add the database call and the invalid-response branch. Moving that rule onto the request model leaves the handler focused on accepting the registration.</p><p>Checking whether an email is already registered may require database I/O. In .NET 10, the handler below awaits that query and constructs its own validation response.</p><p>Minimal API validation in .NET 11 can await <code>AsyncValidationAttribute</code> and <code>IAsyncValidatableObject</code> rules before the handler executes. Register <code>AddValidation()</code>, then put the rule on the request model. <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/validation?view=aspnetcore-11.0#asynchronous-validation-support">ASP.NET Core asynchronous validation documentation</a>.</p><h4><strong>.NET 10</strong></h4><pre><code><code>var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton&lt;IUserStore, DemoUserStore&gt;();

var app = builder.Build();

// .NET 10: the handler itself must call the async rule and translate an
// invalid result into a response. Each endpoint would repeat this wiring.
app.MapPost("/registrations", async (Registration request, IUserStore users, CancellationToken cancellationToken) =&gt;
{
    if (string.IsNullOrWhiteSpace(request.Email))
        return Results.ValidationProblem(new Dictionary&lt;string, string[]&gt; { ["Email"] = ["Email is required."] });

    if (await users.EmailExistsAsync(request.Email, cancellationToken))
        return Results.ValidationProblem(new Dictionary&lt;string, string[]&gt; { ["Email"] = ["Email is already registered."] });

    return Results.Ok(new { message = "Registered", request.Email });
});

app.Run();

public sealed record Registration(string Email);

public interface IUserStore
{
    Task&lt;bool&gt; EmailExistsAsync(string email, CancellationToken cancellationToken);
}

public sealed class DemoUserStore : IUserStore
{
    public async Task&lt;bool&gt; EmailExistsAsync(string email, CancellationToken cancellationToken)
    {
        await Task.Delay(50, cancellationToken); // Simulates asynchronous database I/O.
        return string.Equals(email, "taken@example.com", StringComparison.OrdinalIgnoreCase);
    }
}
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton&lt;IUserStore, DemoUserStore&gt;();
builder.Services.AddValidation();

var app = builder.Build();

// .NET 11 validates the request before this handler runs, awaiting the
// attribute's asynchronous rule and returning a validation problem on failure.
app.MapPost("/registrations", (Registration request) =&gt;
    Results.Ok(new { message = "Registered", request.Email }));

app.Run();

public sealed record Registration([property: Required, UniqueEmail] string Email);

public sealed class UniqueEmailAttribute : AsyncValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext context) =&gt;
        throw new InvalidOperationException("This attribute requires asynchronous validation.");

    protected override async Task&lt;ValidationResult?&gt; IsValidAsync(
        object? value, ValidationContext context, CancellationToken cancellationToken)
    {
        var users = context.GetRequiredService&lt;IUserStore&gt;();
        if (value is string email &amp;&amp; await users.EmailExistsAsync(email, cancellationToken))
            return new ValidationResult("Email is already registered.");

        return ValidationResult.Success;
    }
}

public interface IUserStore
{
    Task&lt;bool&gt; EmailExistsAsync(string email, CancellationToken cancellationToken);
}

public sealed class DemoUserStore : IUserStore
{
    public async Task&lt;bool&gt; EmailExistsAsync(string email, CancellationToken cancellationToken)
    {
        await Task.Delay(50, cancellationToken); // Simulates asynchronous database I/O.
        return string.Equals(email, "taken@example.com", StringComparison.OrdinalIgnoreCase);
    }
}
</code></code></pre><p>The new handler processes successful registrations; <code>UniqueEmailAttribute</code> owns the asynchronous check. Its synchronous <code>IsValid</code> throws because this rule requires async validation. It doesn&#8217;t block a thread by waiting synchronously for the query.</p><p>The attribute gets the registered user store through the validation context using a bit of a bleak service locator anti-pattern. The asynchronous rule runs after the request is bound and before the handler executes, so a rejected email never reaches the registration handler.</p><h3><strong>9. Built-in Zstandard HTTP compression</strong></h3><p>ASP.NET Core 11 adds Zstandard, or <code>zstd</code>, to the default response compression and request decompression providers. The middleware registrations stay the same. You can configure zstd quality through <code>ZstandardCompressionProviderOptions</code>. <a href="https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview3/aspnetcore.md#zstandard-response-compression-and-request-decompression">Microsoft&#8217;s Zstandard HTTP release note</a>.</p><p>The following apps return a text payload and echo request bodies. The .NET 10 built-in providers don&#8217;t support zstd, while the .NET 11 providers do.</p><h4><strong>.NET 10</strong></h4><pre><code><code>var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();

var app = builder.Build();
app.UseResponseCompression();
app.UseRequestDecompression();

// The .NET 10 built-in providers support Brotli and Gzip etc
app.MapGet("/payload", () =&gt; Results.Text(
    string.Concat(Enumerable.Repeat("ASP.NET Core compression example. ", 100)),
    "text/plain"));

app.MapPost("/echo", async (HttpRequest request) =&gt;
{
    using var reader = new StreamReader(request.Body);
    return Results.Text(await reader.ReadToEndAsync(), "text/plain");
});

app.Run();
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>using System.IO.Compression;
using Microsoft.AspNetCore.ResponseCompression;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure&lt;ZstandardCompressionProviderOptions&gt;(options =&gt;
{
    options.CompressionOptions = new ZstandardCompressionOptions { Quality = 6 };
});

var app = builder.Build();
app.UseResponseCompression();
app.UseRequestDecompression();

// ASP.NET Core 11 includes zstd among its default response compression and
// request decompression providers. No custom provider registration is needed.
app.MapGet("/payload", () =&gt; Results.Text(
    string.Concat(Enumerable.Repeat("ASP.NET Core compression example. ", 100)),
    "text/plain"));

app.MapPost("/echo", async (HttpRequest request) =&gt;
{
    using var reader = new StreamReader(request.Body);
    return Results.Text(await reader.ReadToEndAsync(), "text/plain");
});

app.Run();
</code></code></pre><p>Ask each server for zstd and inspect only the response headers:</p><pre><code><code>curl.exe -s -D - -o NUL -H 'Accept-Encoding: zstd' http://localhost:5010/payload
curl.exe -s -D - -o NUL -H 'Accept-Encoding: zstd' http://localhost:5011/payload
</code></code></pre><p>Both responses are <code>200</code>. The first has no <code>Content-Encoding</code> header; the second contains <code>Content-Encoding: zstd</code>. The client must advertise support for the encoding or .NET 11 ignores it.</p><h3><strong>10. Dynamic output-cache policies</strong></h3><p>I can see this being helpful when cache rules need to change while an app keeps running. The provider gets a policy name and can resolve it against current settings. That gives us a place to load or change the rule at runtime, rather than fixing this program&#8217;s duration at startup.</p><p>A named output-cache policy in .NET 10 can be configured at startup with <code>AddPolicy</code>. The first program caches a public catalog response for 30 seconds.</p><p>.NET 11 adds <code>IOutputCachePolicyProvider</code>. The middleware asks the provider to resolve a named policy, letting that provider consult configuration that can change while the app runs. <a href="https://github.com/dotnet/core/blob/main/release-notes/11.0/preview/preview1/aspnetcore.md#ioutputcachepolicyprovider">Microsoft&#8217;s output-cache provider release note</a>.</p><p>The second program includes the entire provider and policy implementation. It keeps the duration in memory and exposes an endpoint to change it.</p><h4><strong>.NET 10</strong></h4><pre><code><code>using Microsoft.AspNetCore.OutputCaching;

var builder = WebApplication.CreateBuilder(args);

// In .NET 10, the named policy is defined when the application starts.
builder.Services.AddOutputCache(options =&gt;
    options.AddPolicy("catalog", policy =&gt;
        policy.Expire(TimeSpan.FromSeconds(30))));

var app = builder.Build();
app.UseOutputCache();

var generationCount = 0;
app.MapGet("/catalog", () =&gt; new
{
    Generation = Interlocked.Increment(ref generationCount),
    GeneratedAt = DateTimeOffset.UtcNow,
    YouTubeSubscriberReminder = "Enjoying the demo? Subscribe for more .NET examples!"
}).CacheOutput("catalog");

app.Run();
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.DependencyInjection.Extensions;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton&lt;CacheDurationStore&gt;();
builder.Services.AddOutputCache();

// The .NET 11 provider resolves the named policy when it is requested.
builder.Services.Replace(
    ServiceDescriptor.Singleton&lt;IOutputCachePolicyProvider, DynamicCachePolicyProvider&gt;());

var app = builder.Build();
app.UseOutputCache();

var generationCount = 0;
app.MapGet("/catalog", (CacheDurationStore store) =&gt; new
{
    Generation = Interlocked.Increment(ref generationCount),
    GeneratedAt = DateTimeOffset.UtcNow,
    CacheSeconds = store.Seconds,
    YouTubeSubscriberReminder = "Enjoying the demo? Subscribe for more .NET examples!"
}).CacheOutput("catalog");

app.MapPost("/cache-seconds/{seconds:int}", async (
    int seconds, CacheDurationStore store, IOutputCacheStore cache) =&gt;
{
    if (seconds is &lt; 1 or &gt; 120)
    {
        return Results.BadRequest("Choose a duration from 1 to 120 seconds.");
    }

    store.Seconds = seconds;
    await cache.EvictByTagAsync("catalog", CancellationToken.None);
    return Results.Ok(new { CacheSeconds = seconds });
});

app.Run();

sealed class CacheDurationStore
{
    private int _seconds = 30;

    public int Seconds
    {
        get =&gt; Volatile.Read(ref _seconds);
        set =&gt; Volatile.Write(ref _seconds, value);
    }
}

sealed class DynamicCachePolicyProvider(CacheDurationStore store) : IOutputCachePolicyProvider
{
    public IReadOnlyList&lt;IOutputCachePolicy&gt; GetBasePolicies() =&gt; [];

    public ValueTask&lt;IOutputCachePolicy?&gt; GetPolicyAsync(string policyName)
    {
        if (policyName != "catalog")
        {
            return ValueTask.FromResult&lt;IOutputCachePolicy?&gt;(null);
        }

        IOutputCachePolicy policy = new DynamicDurationPolicy(store.Seconds);

        return ValueTask.FromResult&lt;IOutputCachePolicy?&gt;(policy);
    }
}

sealed class DynamicDurationPolicy(int seconds) : IOutputCachePolicy
{
    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
    {
        context.EnableOutputCaching = true;
        context.AllowCacheLookup = true;
        context.AllowCacheStorage = true;
        context.AllowLocking = true;
        context.Tags.Add("catalog");
        context.ResponseExpirationTimeSpan = TimeSpan.FromSeconds(seconds);
        return ValueTask.CompletedTask;
    }

    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
        =&gt; ValueTask.CompletedTask;

    public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
    {
        context.ResponseExpirationTimeSpan = TimeSpan.FromSeconds(seconds);
        return ValueTask.CompletedTask;
    }
}
</code></code></pre><p>The provider creates <code>DynamicDurationPolicy</code> from the current duration. The policy enables lookup and storage, sets the expiration, and tags the cached response as <code>catalog</code>.</p><p>Run these requests against the .NET 11 app:</p><pre><code><code>Invoke-RestMethod http://localhost:5011/catalog
Invoke-RestMethod http://localhost:5011/catalog
Invoke-RestMethod -Method Post http://localhost:5011/cache-seconds/5
Invoke-RestMethod http://localhost:5011/catalog
</code></code></pre><p>The first two GETs have the same <code>Generation</code> while the response is cached. The POST updates the duration and evicts the tagged entry. The next GET generates a fresh response and reports <code>CacheSeconds</code> as <code>5</code>.</p><p>Changing the duration doesn&#8217;t remove an existing entry by itself. The explicit eviction makes the update visible immediately. This program caches one public response; tenant-specific responses need cache keys that distinguish their contents.</p><h2><strong>.NET 11 runtime</strong></h2><h3><strong>11. Runtime-native async</strong></h3><p>Runtime Async V2 moves suspension and resumption into the runtime. Application code still uses <code>async</code> and <code>await</code>, but the execution method changes. One visible effect is the live stack trace where application methods appear more directly without the compiler-generated async infrastructure between them. <a href="https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-11/runtime#runtime-async">Microsoft&#8217;s Runtime Async documentation</a>.</p><p>These programs call <code>OuterAsync</code>, then <code>MiddleAsync</code>, then <code>InnerAsync</code>, which prints a live <code>StackTrace</code>.</p><h4><strong>.NET 10</strong></h4><pre><code><code>using System.Diagnostics;

await OuterAsync();

static async Task OuterAsync()
{
    await Task.CompletedTask;
    await MiddleAsync();
}

static async Task MiddleAsync()
{
    await Task.CompletedTask;
    await InnerAsync();
}

static async Task InnerAsync()
{
    await Task.CompletedTask;
    Console.WriteLine("Live stack with compiler-generated async:");
    Console.WriteLine(new StackTrace(fNeedFileInfo: true));
}
</code></code></pre><h4><strong>.NET 11</strong></h4><pre><code><code>using System.Diagnostics;

await OuterAsync();

static async Task OuterAsync()
{
    await Task.CompletedTask;
    await MiddleAsync();
}

static async Task MiddleAsync()
{
    await Task.CompletedTask;
    await InnerAsync();
}

static async Task InnerAsync()
{
    await Task.CompletedTask;
    Console.WriteLine("Live stack with runtime-native async:");
    Console.WriteLine(new StackTrace(fNeedFileInfo: true));
}
</code></code></pre><p>Runtime async remains an opt-in preview feature. For the .NET 11 program, use this complete project file:</p><pre><code><code>&lt;Project Sdk="Microsoft.NET.Sdk"&gt;
  &lt;PropertyGroup&gt;
    &lt;OutputType&gt;Exe&lt;/OutputType&gt;
    &lt;TargetFramework&gt;net11.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
    &lt;Features&gt;$(Features);runtime-async=on&lt;/Features&gt;
  &lt;/PropertyGroup&gt;
&lt;/Project&gt;
</code></code></pre><p>Run each console app in Debug configuration:</p><pre><code><code>dotnet run --project Demo10 -c Debug
dotnet run --project Demo11 -c Debug
</code></code></pre><p>The .NET 10 trace includes infrastructure frames such as <code>AsyncMethodBuilderCore.Start</code>. With runtime async enabled, the application call chain appears more directly. Build settings and JIT optimizations affect the number of frames, so compare the method chain.</p><p>A <code>net11.0</code> project doesn&#8217;t also need <code>EnablePreviewFeatures</code> for this opt-in. Exception stack traces already receive cleanup; the code inspects a live stack to show the diagnostic change.</p><p>This should basically be a free speedup for any async heavy apps.</p><h2><strong>Closing thoughts</strong></h2><p>I think it&#8217;s time to slow down on the new syntax in C# 16. .NET 12 needs to compete with Rust etc for the attention of the AI pilled vibe coders, even within Microsoft. Fix AoT and make it competitive with Rust and Go.</p>]]></content:encoded></item><item><title><![CDATA[C# 14's best new features]]></title><description><![CDATA[This stuff you might actually use]]></description><link>https://www.edandersen.com/p/c-14s-best-new-features</link><guid isPermaLink="false">https://www.edandersen.com/p/c-14s-best-new-features</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Sun, 21 Sep 2025 04:26:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6898d3d0-10d2-403a-b84c-58e0eb3cf469_840x600.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi all. Been a while since I&#8217;ve done one of these. Thanks for continuing to subscribe! And if you&#8217;d like this post in cringe Youtube video form, check out:</p><div id="youtube2-RTFdUBKQres" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;RTFdUBKQres&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/RTFdUBKQres?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><h3>1. Null-Conditional Assignment (<code>?=</code>)</h3><p>We've all written this boilerplate code a thousand times. You have a nullable object, and you only want to assign a value to one of its properties if the object itself isn't <code>null</code>.</p><p>In C# 13, you had to perform an explicit null check.</p><pre><code><code>public class Channel
{
    public int Subscribers { get; set; }
    public int Members { get; set; }
}

// ---

Channel? myChannel = null; // or new Channel();

// We have to check for null before assigning
if (myChannel is not null)
{
    myChannel.Subscribers = 1000;
}
</code></code></pre><p><strong>After: C# 14</strong></p><p>With the new null-conditional assignment operator (<code>?=</code>), you can do this in a single, safe, and readable line.</p><pre><code><code>Channel? myChannel = null;

// This will only assign 1000 if myChannel is not null
myChannel?.Subscribers = 1000;
</code></code></pre><p>It's a small change, but a very welcome one!</p><div><hr></div><h3>2. The <code>field</code> Keyword for Semi-Auto-Properties</h3><p>Auto-properties are great, but as soon as you need to add logic to your getter or setter (like validation), you have to convert it to a full property with an explicit private backing field.</p><p><strong>Before: C# 13</strong></p><p>This required creating a separate field (<code>_subscribers</code>) just to add a check in the setter.</p><pre><code><code>public class Channel
{
    private int _subscribers;
    public int Subscribers
    {
        get =&gt; _subscribers;
        set
        {
            if (value &lt; 1000)
            {
                throw new ArgumentException("Not enough subs!");
            }
            _subscribers = value;
        }
    }
}
</code></code></pre><p><strong>After: C# 14</strong></p><p>The new <code>field</code> keyword gives you access to the implicit, compiler-generated backing field of an auto-property. This means you can add logic without the boilerplate!</p><pre><code><code>public class Channel
{
    public int Subscribers
    {
        get =&gt; field; // 'field' refers to the implicit backing field
        set
        {
            if (value &lt; 1000)
            {
                throw new ArgumentException("Not enough subs!");
            }
            field = value; // Assign to the implicit backing field
        }
    }
}
</code></code></pre><p>This keeps the code much cleaner and more concise.</p><div><hr></div><h3>3. User-Defined Compound Operators</h3><p>This one is really cool. Have you ever wanted to define what a compound operator like <code>++</code> or <code>+=</code> does for your own custom type? Now you can!</p><p>Imagine we want the <code>++</code> operator to increment both the <code>Subscribers</code> and <code>Members</code> properties of our <code>Channel</code> class at the same time.</p><p><strong>Before: C# 13</strong></p><p>You'd have to increment each property manually.</p><pre><code><code>var myChannel = new Channel();
myChannel.Subscribers++;
myChannel.Members++;
</code></code></pre><p><strong>After: C# 14</strong></p><p>You can now overload the compound operator directly in your class definition.</p><pre><code><code>public class Channel
{
    public int Subscribers { get; set; }
    public int Members { get; set; }

    public void operator ++()
    {
        Subscribers++;
        Members++;
    }

    public void operator +=(int amount)
    {
        Subscribers += amount;
        Members += amount;
    }
}

// ---

var myChannel = new Channel();
myChannel++; // This now increments both Subscribers and Members!

Console.WriteLine($"Subs: {myChannel.Subscribers}, Members: {myChannel.Members}");
// Output: Subs: 1, Members: 1
</code></code></pre><p>Not sure where I&#8217;d use this but hey ho!</p><div><hr></div><h3>4. The <code>extension</code> Keyword</h3><p>Extension methods have always required a <code>public static</code> class and <code>public static</code> methods using the <code>this</code> keyword. C# 14 introduces a new, more explicit syntax.</p><p><strong>Before: C# 13</strong></p><p>The classic syntax we all know.</p><pre><code><code>public static class ChannelExtensions
{
    public static void PrintDetails(this Channel c)
    {
        Console.WriteLine($"Subs: {c.Subscribers}, Members: {c.Members}");
    }
}
</code></code></pre><p><strong>After: C# 14</strong></p><p>You can now use the <code>extension</code> keyword to create an "extension type." The syntax is cleaner and makes the intent clearer.</p><pre><code><code>public extension ChannelExtensions
{
    public void PrintDetails(this Channel c)
    {
        Console.WriteLine($"Subs: {c.Subscribers}, Members: {c.Members}");
    }
}
</code></code></pre><p>Personally, I'm not totally sold on this one yet, as it just creates another way to do the same thing. You can now use extension methods to add Properties, but still&#8230;</p><div><hr></div><h3>5. <code>nameof</code> Improvements for Generics</h3><p>The <code>nameof</code> operator is a handy tool for getting the string name of a variable, type, or member. However, it had a limitation with generic types.</p><p><strong>Before: C# 13</strong></p><p><code>nameof</code> worked on a constructed generic type, but not on an "unbound" or open generic type.</p><p>C#</p><pre><code><code>public class Channel&lt;T&gt; { }
public class YouTube { }

// This worked fine
Console.WriteLine(nameof(Channel&lt;YouTube&gt;)); // Output: Channel

// This caused a compile error
// Console.WriteLine(nameof(Channel&lt;&gt;));
</code></code></pre><p><strong>After: C# 14</strong></p><p>This limitation has been removed. You can now use <code>nameof</code> directly on an unbound generic type.</p><p>C#</p><pre><code><code>// Now this works perfectly!
Console.WriteLine(nameof(Channel&lt;&gt;)); // Output: Channel
</code></code></pre><p>This is a nice little fix for those edge cases where you need the name of the generic type definition itself.</p><div><hr></div><h3>My thoughts on C# 14</h3><p>Actually they&#8217;ve been quite restrained this year. No crazy new ways to instantiate lists or classes that conceptually forks C#. I do get alot of comments on my channel saying that C# is getting too complicated, so glad they have shown restraint. Well done chaps!</p>]]></content:encoded></item><item><title><![CDATA[The state of WPF in 2025]]></title><description><![CDATA[Reports of WPF's death are exaggerated]]></description><link>https://www.edandersen.com/p/the-state-of-wpf-in-2025</link><guid isPermaLink="false">https://www.edandersen.com/p/the-state-of-wpf-in-2025</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 20 Jun 2025 03:17:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zLYg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Deep dive this week into an important question that Redditors get in a panic about  quite often - is WPF Dead? </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zLYg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zLYg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 424w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 848w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1272w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zLYg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png" width="1456" height="727" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:727,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:833795,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/166371771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zLYg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 424w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 848w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1272w, https://substackcdn.com/image/fetch/$s_!zLYg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0dab0169-c641-4e39-ab50-e903bea833d1_1506x752.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>Let's look at the full state of WPF today.</p><h3>Alive and Well in the Enterprise and &#8220;boring&#8221; stuff</h3><p>First things first: Microsoft is still actively working on WPF. It was made part of the open-source .NET Foundation and continues to receive meaningful updates. In .NET 8, we got hardware acceleration for RDP and a new folder dialog. For .NET 9, Microsoft is adding a Fluent theme to make WPF apps look more like modern Windows 11 applications, a feature many thought was reserved for newer frameworks. The official roadmap even shows plans for .NET 10. This isn't just life support; it's active development. This predictability is precisely what enterprises value, which is why you'll find WPF powering critical line-of-business applications on oil rigs, in maritime shipping, and across the manufacturing industry&#8212;the so-called "dark matter developers" who don't always show up in surveys but build the backbone of industry.</p><ul><li><p><a href="https://www.google.com/search?q=https://devblogs.microsoft.com/dotnet/whats-new-for-wpf-in-dotnet-8/">What's new for WPF in .NET 8 - Microsoft Dev Blogs</a></p></li><li><p><a href="https://github.com/dotnet/wpf/blob/main/roadmap.md">WPF Roadmap 2024 - GitHub</a></p></li><li><p><a href="https://www.google.com/search?q=https://learn.microsoft.com/en-us/dotnet/desktop/wpf/introduction/wpf-overview%3Fview%3Dnetdesktop-9.0">Windows Presentation Foundation - Microsoft Docs</a></p></li></ul><h3>Third parties make bank on WPF</h3><p>WPF&#8217;s longevity isn&#8217;t just thanks to Microsoft. I&#8217;d wager that third parties make more money from WPF than MS does themselves and are incentivized to keep it that way.<br><br>A massive ecosystem of vendors and open-source projects keeps it vibrant. Companies like Telerik and DevExpress continue to sell and support extensive component libraries, investing thousands to ensure developers can build complex, feature-rich applications quickly. At the same time, the open-source community is keeping things fresh. Before MS added the Fluent theme to WPF, popular projects like MahApps.Metro give WPF apps a modern Windows look and feel like Windows 10 rather than the classic WebForms inspired take, while Material Design in XAML allows you to build a Windows app that looks like an Android app. This combination of corporate and community support is a self perpetuating machine.<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gz1Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gz1Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 424w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 848w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1272w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png" width="991" height="778" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:778,&quot;width&quot;:991,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:666608,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/166371771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gz1Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 424w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 848w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1272w, https://substackcdn.com/image/fetch/$s_!gz1Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4160b40-71f0-400e-945d-ae78fb6a9099_991x778.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Pay up lads</figcaption></figure></div><ul><li><p><a href="https://www.telerik.com/blogs/wpf-net-maui-how-choose">WPF vs. .NET MAUI: How to Choose in 2024 - Telerik</a></p></li><li><p><a href="https://www.devexpress.com/wpf/">WPF UI Controls - DevExpress</a></p></li><li><p><a href="https://www.google.com/search?q=https://github.com/MaterialDesignInXAML/MaterialDesignInXAML">Material Design in XAML Toolkit - GitHub</a></p></li><li><p><a href="https://github.com/MahApps/MahApps.Metro">MahApps.Metro - GitHub</a></p></li></ul><h3>The Geographic Divide and the Ghost of WinUI</h3><p>So if it's not dead, where is it hiding? I think the debate is still a thing because  because WPF's popularity is highly regional. <a href="https://www.ziprecruiter.com/Salaries/WPF-Developer-Salary">In the United States, the average salary for a WPF developer is a chonky $130,000</a>, suggesting strong demand. But cross the pond to the UK, and the number of job postings mentioning WPF is vanishingly small. In Japan, they're practically non-existent outside of niche finance roles. Surprisingly, Google Trends data shows a huge resurgence of interest in China and South Korea. Meanwhile, Microsoft's intended successor, WinUI, has seen development activity fall off a cliff since late 2021, with its GitHub commit history looking like a ghost town compared to the flurry of activity around .NET MAUI, Avalonia, and even WPF itself.</p><p>I miss WinUI btw. Seriously. WinUI 2.x was fast, compiled native and created apps indistinguishable from the Windows 10 and Windows 11 shells. In my view if you cannot create the native Settings app pixel for pixel perfect then its not a &#8220;native&#8221; app toolkit.</p><ul><li><p><a href="https://www.google.com/search?q=https://www.scichart.com/the-future-of-wpf-is-wpf-dead/">The Future of WPF: Is WPF Dead? - SciChart</a></p></li><li><p><a href="https://github.com/microsoft/microsoft-ui-xaml">microsoft-ui-xaml (WinUI) Repository - GitHub</a></p></li></ul><h3>The Spirit Lives On: Avalonia and OpenSilver</h3><p>While WPF remains a Windows-only affair, its XAML-based spirit is finding new life in cross-platform frameworks. <a href="https://avaloniaui.net/">Avalonia</a> proudly calls itself the open-source WPF successor and even offers a product, XPF, designed to take your existing WPF applications and run them on macOS and Linux in minutes. It&#8217;s what Microsoft tried&#8212;and failed&#8212;to do with "WPF Everywhere" back in 2006, a project that morphed into the ill-fated Silverlight. And just like WPF, Silverlight's spirit has been resurrected by the community in the form of OpenSilver, which is picking up where Microsoft left off. For developers who honed their XAML skills on WPF, these frameworks offer an exciting path forward without abandoning their expertise.<br></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oLBF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oLBF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 424w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 848w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1272w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oLBF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png" width="1242" height="857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:857,&quot;width&quot;:1242,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:341676,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/166371771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oLBF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 424w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 848w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1272w, https://substackcdn.com/image/fetch/$s_!oLBF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66984d79-6db0-4271-a497-321ad22bcd1f_1242x857.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Avalonia keeping the dream alive</figcaption></figure></div><ul><li><p><a href="https://opensilver.net/">OpenSilver - A modern, open-source reimplementation of Silverlight</a></p></li><li><p><a href="https://www.google.com/search?q=https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/wpf">Xamarin.Forms Platform Support - Microsoft Docs (Historical)</a></p></li></ul><h3>The Verdict</h3><p>So, is WPF dead? <strong>No.</strong> But it has probably flatlined.<br><br>Its spirit, however, has broken free from its Windows-only confines and lives on in modern, cross-platform frameworks. The transferrable design patterns, XAML, MVVM etc are reusable across Uno and Avalonia today, even as .NET MAUI sails off into the sunset.</p><div><hr></div><p><br>View this post in mega cringe video form:<br></p><div id="youtube2-aaKgNKLpeGA" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;aaKgNKLpeGA&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/aaKgNKLpeGA?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><br><br></p>]]></content:encoded></item><item><title><![CDATA[JetBrains ReSharper comes to VS Code, SQL Server 2025 actually looks good]]></title><description><![CDATA[Teds Tech, May 29th 2025]]></description><link>https://www.edandersen.com/p/jetbrains-resharper-comes-to-vs-code</link><guid isPermaLink="false">https://www.edandersen.com/p/jetbrains-resharper-comes-to-vs-code</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 29 May 2025 10:06:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What a week. If you haven&#8217;t seen it, I missed some cracking news from MS Build which was released about 3 hours before going to &#8220;print&#8221; last week - loose C# file execution without a .csproj file. I made a video about it here:</p><div id="youtube2-QWYFGdEeCkQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;QWYFGdEeCkQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/QWYFGdEeCkQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Onto this week&#8217;s news: </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lErf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lErf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 424w, https://substackcdn.com/image/fetch/$s_!lErf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 848w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1272w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lErf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png" width="1131" height="667" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:667,&quot;width&quot;:1131,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:108550,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/164712246?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lErf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 424w, https://substackcdn.com/image/fetch/$s_!lErf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 848w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1272w, https://substackcdn.com/image/fetch/$s_!lErf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e81288-2b86-42d6-9b35-52c0c0c09908_1131x667.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><br>JetBrains releases ReSharper for VS Code</h2><p>For veteran developers, the news might bring a wave of nostalgia. ReSharper was, for many, an indispensable part of the C# development experience. The cry of "Please, a ReSharper license!" from a new team member was expected.</p><p>I&#8217;ve given it a go and you need to disable Microsoft's own C# and C# Dev Kit extensions, as ReSharper apparently prefers to take full command of the C# experience. This in itself signals the potential for a fascinating dynamic in the VS Code tooling space.</p><p>Once installed and your solution is loaded (perhaps after the classic ReSharper cache processing warms up, a sight familiar to Visual Studio users), the experience aims to be classic ReSharper. The "ReSharper Solution Explorer" makes an appearance, which basically mirrors the C# Dev Kit Solution Explorer. I had great success with the iconic "Initialize field from constructor" shortcut, a beloved time-saver, and it works just as well as it did in VS.</p><p>This move by JetBrains is particularly interesting given Microsoft's recent efforts to monetize the C# experience in VS Code through the C# Dev Kit, which is tied into Visual Studio subscriptions. I do think this will get somehow squashed by MS, there was a reason JetBrains announced in 2021 that they weren&#8217;t going to bother.</p><p><strong>Relevant Links:</strong></p><ul><li><p>JetBrains ReSharper in VS Code Public Preview Announcement <a href="https://blog.jetbrains.com/dotnet/2025/05/19/resharper-comes-to-microsoft-visual-studio-code/">https://blog.jetbrains.com/dotnet/2025/05/19/resharper-comes-to-microsoft-visual-studio-code/</a></p></li><li><p>ReSharper on the Visual Studio Code Marketplace: <a href="https://marketplace.visualstudio.com/items?itemName=JetBrains.ReSharper-VSCode">https://marketplace.visualstudio.com/items?itemName=JetBrains.ReSharper-VSCode</a></p></li></ul><h2>SQL Server 2025 gets decent JSON support</h2><p>I&#8217;ll ignore the AI stuff for this update.</p><p>Fascinating to me is the new <code>sp_invoke_external_rest_endpoint</code> stored procedure. This has serious potential. For years, reaching out to external web services from within SQL Server has been a clunky affair, often requiring complex CLR assemblies or other workarounds. Now, developers will be able to directly call secure HTTPS REST endpoints (supporting GET, POST, PUT, etc.) from stored procedures, triggers, or functions, and directly process the response. Imagine enriching your data in real-time by calling an external API, or triggering a workflow in another system based on a database event &#8211; all natively within SQL Server. Scary.</p><p>Complementing this external connectivity is a long-awaited enhancement to JSON support. SQL Server has had rudimentary JSON capabilities for a while &#8211; parsing with <code>OPENJSON</code> and formatting with <code>FOR JSON</code> &#8211; but it always felt like a bolt-on rather than a first-class citizen. Developers often looked to databases like PostgreSQL for more robust native JSON handling. With SQL Server 2025, that changes. The introduction of a native JSON data type aims to bring SQL Server to feature parity with competitors, allowing for more efficient storage, indexing, and querying of JSON documents directly within the relational database.</p><p>When you combine these two features &#8211; the ability to call a REST API and get a JSON response, and then store and manipulate that JSON natively &#8211; the possibilities are quite exciting, and perhaps a little "scary" in terms of the complex logic that might now reside in the database tier. </p><p>The "AI-ready" stuff  extends to integrated vector database querying capabilities and other GenAI model integrations and obviously they added GitHub Copilot to the beloved SQL Server Management Studio. </p><p><strong>Relevant Links:</strong></p><ul><li><p>Official SQL Server 2025 Public Preview Blog: <a href="https://cloudblogs.microsoft.com/sqlserver/2025/05/19/announcing-sql-server-2025-public-preview/">https://cloudblogs.microsoft.com/sqlserver/2025/05/19/announcing-sql-server-2025-public-preview/ </a></p></li><li><p>SQL Server 2025 Public Preview Discussion on Reddit: <a href="https://www.reddit.com/r/SQLServer/comments/1kqfecc/announcing_the_public_preview_of_sql_server_2025/">https://www.reddit.com/r/SQLServer/comments/1kqfecc/announcing_the_public_preview_of_sql_server_2025/</a></p></li></ul><h2>Windows Update Gets an Update</h2><p>In a spell of wishful thinking, MS looks like they&#8217;ll try to cajole the 1000s of disparate update mechanisms on Windows to all hook into Windows Update.</p><p>This sounds like a nightmare to me but you can read more about it on &#8220;Introducing a Unified Future for App Updates on Windows&#8221; at their blog: <a href="https://techcommunity.microsoft.com/blog/windows-itpro-blog/introducing-a-unified-future-for-app-updates-on-windows/4416354">https://techcommunity.microsoft.com/blog/windows-itpro-blog/introducing-a-unified-future-for-app-updates-on-windows/4416354</a></p><div><hr></div><p>Thanks for reading and continuing to subscribe. For a video version of this post, check out:</p><div id="youtube2-975HcGOTix4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;975HcGOTix4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/975HcGOTix4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Microsoft Build 2025 updates for .NET developers]]></title><description><![CDATA[Ted's Tech, May 23rd 2025]]></description><link>https://www.edandersen.com/p/microsoft-build-2025-updates-for</link><guid isPermaLink="false">https://www.edandersen.com/p/microsoft-build-2025-updates-for</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 23 May 2025 08:13:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!AG_0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Microsoft Build 2025 has wrapped up and I&#8217;ve done my best to find something, anything, that might be of interest to .NET/C# developers.  It really was tough.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AG_0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AG_0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 424w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 848w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1272w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AG_0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png" width="917" height="516" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:516,&quot;width&quot;:917,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:359082,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/164059378?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AG_0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 424w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 848w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1272w, https://substackcdn.com/image/fetch/$s_!AG_0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8714ca07-f276-4cf6-8e1f-d8f7428c8a77_917x516.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Azure AI Foundry Local</h2><p>One significant announcement is <strong>Azure Foundry Local</strong>. Think of it as a mini-version of Ollama, allowing you to run AI models locally. It's designed to work seamlessly with <strong>Azure AI Foundry</strong> and even includes an <strong>SDK for C#</strong> (though it's not yet on NuGet). This means you can integrate AI capabilities directly into your C# and .NET applications, running models on your local machine, even with full Mac GPU support. Imagine asking a question like, "Is .NET and C# cool and popular?" and getting an AI-powered "Yes!" from your local setup just to help alleviate your anxiety.</p><p>It provides a fully OpenAI Chat Completion API compatible local endpoint when you run it, so you can actually point existing apps (assuming you can change the model name) at it and get started.</p><p>The docs are here: <a href="https://learn.microsoft.com/en-gb/azure/ai-foundry/foundry-local/get-started">https://learn.microsoft.com/en-gb/azure/ai-foundry/foundry-local/get-started</a></p><h2><br><br>.NET Aspire rolls on to 9.3</h2><p>No Blazor, MVC, Razor Pages or any other content really - just .NET Aspire.</p><p>In a sign of insane irony, they&#8217;ve actually embedded GitHub Copilot into the Aspire Dashboard, which loops through the C# Dev Kit and the GitHub Copilot extension in VS Code.</p><p>I know the KPIs at MS are brutal but this is really quite something.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J0O5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J0O5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 424w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 848w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1272w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J0O5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png" width="761" height="841" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:841,&quot;width&quot;:761,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:232948,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/164059378?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!J0O5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 424w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 848w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1272w, https://substackcdn.com/image/fetch/$s_!J0O5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc151fc2d-251d-4586-a37a-835c2cbf374f_761x841.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Azure App Service: Faster, Cheaper, Better!</h3><p>Now for something positive! Azure App Service is easily my favourite service on Azure that just keeps getting better. Microsoft announced a new service plan that promises significant improvements:</p><ul><li><p><strong>24% cost savings on Windows</strong> for better cost-performance.</p></li><li><p><strong>Faster temporary storage</strong>.</p></li><li><p>A wider range of CPU and RAM options, from modest setups to massive 256GB configurations.</p></li><li><p>Finally, <strong>high availability with only two availability zones</strong>. This means you no longer need three nodes on your web tier to achieve a <strong>99.99% SLA</strong>, potentially cutting a third off your App Service bills.</p></li></ul><p>Most projects, and I do mean like over 90% of them, should be running on Azure App Service in my opinion. It really is that good.</p><div><hr></div><p>Thank you for continuing to subscribe to the newsletter. If you&#8217;d like this post in slightly ironic video form, check out the below:<br></p><div id="youtube2-0nYY_WIJ6YU" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;0nYY_WIJ6YU&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/0nYY_WIJ6YU?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[VS Code is no longer an IDE, layoffs hit MS teams and .NET 10 Preview 4 is out]]></title><description><![CDATA[Teds Tech, May 15th 2025]]></description><link>https://www.edandersen.com/p/vs-code-is-no-longer-an-ide-layoffs</link><guid isPermaLink="false">https://www.edandersen.com/p/vs-code-is-no-longer-an-ide-layoffs</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Thu, 15 May 2025 08:05:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Rx0f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Its been a couple of weeks since I&#8217;ve done one of these as I was away on a business trip last week. Regular service resumes below!</p><h3>VS Code 1.100 sees GitHub Copilot get absorbed totally</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Rx0f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Rx0f!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 424w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 848w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1272w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png" width="1129" height="632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:632,&quot;width&quot;:1129,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:158259,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Rx0f!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 424w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 848w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1272w, https://substackcdn.com/image/fetch/$s_!Rx0f!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3f05616-f2dd-4c83-a1e1-6971a3363719_1129x632.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In a masterclass of product reclassification, VS Code is now being referred to as an &#8220;AI Editor&#8221;. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!C2xQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!C2xQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 424w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 848w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1272w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png" width="600" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:600,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44914,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!C2xQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 424w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 848w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1272w, https://substackcdn.com/image/fetch/$s_!C2xQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6745e3dd-0a8b-45cf-81f6-f270cd97a8ad_600x252.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Tracing the history here is fascinating. In June 2023, v1.80 release notes still referred to the GitHub Copilot as a separate extension:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PEN8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PEN8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 424w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 848w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1272w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PEN8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png" width="652" height="303" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:303,&quot;width&quot;:652,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50443,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PEN8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 424w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 848w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1272w, https://substackcdn.com/image/fetch/$s_!PEN8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F704e8486-0dbd-4bd1-8d20-2859df7cd306_652x303.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>But by v1.100, updates to the AI chat function is now indistinguishable from core functionality:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pY09!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pY09!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 424w, https://substackcdn.com/image/fetch/$s_!pY09!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 848w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1272w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pY09!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png" width="652" height="299" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:299,&quot;width&quot;:652,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:57512,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pY09!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 424w, https://substackcdn.com/image/fetch/$s_!pY09!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 848w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1272w, https://substackcdn.com/image/fetch/$s_!pY09!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50e27707-a41a-473e-9d7d-fe62d5f272f4_652x299.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>My view is that this is purely a response to Cursor and Windsurf. The GitHub Copilot extension is not open source, and now VS Code bundles a huge closed source extension along with it. VS Code is no longer &#8220;open source&#8221; by any definition.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/HA520/4/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2cd2628-2dbc-41fb-94d1-d67cebf61cef_1260x660.png&quot;,&quot;thumbnail_url_full&quot;:&quot;&quot;,&quot;height&quot;:400,&quot;title&quot;:&quot;VS Code subsumes GitHub Copilot timeline&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/HA520/4/" width="730" height="400" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><h3>Layoffs hit MAUI, TypeScript teams</h3><p>Just in time for Microsoft Build next week, Microsoft has decided to lay off engineers across the firm, including &#8220;folks&#8221; in their TypeScript and .NET Android and MAUI teams. I believe the .NET MAUI team has been totally gutted.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k6Jg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k6Jg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 424w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 848w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1272w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png" width="602" height="235" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/718a863f-89f1-471b-a26a-77564a95d707_602x235.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:235,&quot;width&quot;:602,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:36278,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!k6Jg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 424w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 848w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1272w, https://substackcdn.com/image/fetch/$s_!k6Jg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F718a863f-89f1-471b-a26a-77564a95d707_602x235.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>This poor chap has been at Microsoft for 18 years and worked on the TypeScript compiler being rewritten in Go:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xznX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xznX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 424w, https://substackcdn.com/image/fetch/$s_!xznX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 848w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1272w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xznX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png" width="616" height="236" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:236,&quot;width&quot;:616,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44690,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/163511991?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xznX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 424w, https://substackcdn.com/image/fetch/$s_!xznX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 848w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1272w, https://substackcdn.com/image/fetch/$s_!xznX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0896ccdc-4bdb-4cb0-87fb-eb7554a23cae_616x236.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>I guess the beatings will continue until morale improves.</p><h2>.NET 10 Preview 4 </h2><p>Two main new features I like in this are new async methods for Zip folder creation, plus Minimal APIs get another boost with validation for Record types.</p><p>You can now do the following:</p><p><code>using System.IO.Compression;</code></p><p><code>await ZipFile.CreateFromDirectoryAsync("photos", "we-have-to-go-back-async.zip");</code></p><p>Whereas you were stuck with non-async versions in .NET 8.</p><div><hr></div><p>As always, I do appreciate you continuing to subscribe. For a more visual version of this post, check out the YouTube version below: </p><div id="youtube2-93pxUFZgS9g" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;93pxUFZgS9g&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/93pxUFZgS9g?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Github Copilot for upgrading .NET projects and querying Azure - .NET Conf recap]]></title><description><![CDATA[Ted's Tech, April 25th 2025]]></description><link>https://www.edandersen.com/p/github-copilot-for-upgrading-net</link><guid isPermaLink="false">https://www.edandersen.com/p/github-copilot-for-upgrading-net</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 25 Apr 2025 01:16:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!z1yT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Thanks for continuing to subscribe to the newsletter! The weather has picked up in Japan this week as we go truly into spring. Japan has four distinct seasons I am told repeatedly and they are not wrong.</p><p>Onto the Microsoft developer content&#8230;</p><h2>.NET Conf &#8220;Focus on Modernisation&#8221;</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z1yT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z1yT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 424w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 848w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1272w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z1yT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png" width="970" height="576" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:576,&quot;width&quot;:970,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:345407,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.edandersen.com/i/161838420?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z1yT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 424w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 848w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1272w, https://substackcdn.com/image/fetch/$s_!z1yT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6d00c0e-5afb-4c35-9594-66d21e46a147_970x576.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>CSharp &#8220;Jeff&#8221; Fritz hosted a special .NET Conf this week aimed at showing the latest techniques and tools available to upgrade the considerable estate of legacy .NET apps out there. Even Scott Hanselman dug out his <a href="https://www.hanselman.com/babysmash/">Baby Smash app</a> for an update!</p><p>A central tool highlighted was the .NET Upgrade Assistant, available as a Visual Studio extension and a CLI tool, designed to analyze projects, identify potential issues, and aid in the upgrade process for various project types like WPF, WinForms, and older ASP.NET MVC apps<strong>. </strong>The Upgrade Assistant isn&#8217;t new - its been around for donkeys years - but they have now added Github Copilot to it to provide AI generated agent-like upgrade step generation. </p><p>I remain skeptical about AI being help to help much here considering the lack of publicly available training data for this process - upgrades from .NET Framework to the latest .NET versions are the equivalent of open heart surgery and to do correctly is one of the hardest things a .NET Developer can do. It requires over a decade of archeological knowledge about .NET versions and their upgrade paths - where is this training data coming from? CodeProject blog posts?</p><p>One wild demo was the ability to embed Blazor components in a classic WebForms app. I highly suggest you watch the video as it really was a tour de force of proper .NET boomer content: </p><div id="youtube2-ZwmD__W8UFM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;ZwmD__W8UFM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/ZwmD__W8UFM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Azure releases its own MCP server</h2><p>Previously this functionality was limited to Copilot within Azure itself, but to score internet points the Azure team have released a new MCP server that allows local agents, either Copilot in Agent mode, Claude, Cursor or other MCP clients to access information about your Azure infrastructure.</p><p>What&#8217;s more impressive is that it is built in .NET 9 and C#! Yes! No JavaScript or Go cop-outs here. What is wild is the way they&#8217;ve <a href="https://github.com/Azure/azure-mcp/tree/main/eng/npm">wrapped npx and the node package manager around calling different .NET binaries for each platform</a>. I think its time that .NET had an <code>npx</code> equivalent.</p><p>They have been good and for now restricted the operations to read-only. But this is only a short leap away from the MCP server being able to do tasks like creating resources and then its just another short leap away from deleting them. The team know they are on the edge of something pretty crazy here, so have added one of the best worded disclaimers I&#8217;ve ever seen:</p><blockquote><p>MCP as a phenomenon is very novel and cutting-edge. As with all new technology standards, consider doing a security review to ensure any systems that integrate with MCP servers follow all regulations and standards your system is expected to adhere to. This includes not only the Azure MCP Server, but any MCP client/agent that you choose to implement down to the model provider.</p></blockquote><p>Basically trying to pass on any blame if something goes wrong.</p><p>One amazing example of Microsoft left hand not talking to the right hand is that the MCP server they&#8217;ve built isn&#8217;t currently compatible with their own Semantic Kernel framework due to the function names <a href="https://github.com/Azure/azure-mcp/issues/42">having hyphens instead of underscores</a>, which means they&#8217;ve only tested it using the VSCode/Github Copilot MCP client and not their own .NET agents framework. Classic.</p><p><strong>Azure MCP</strong>: <a href="https://github.com/Azure/azure-mcp">https://github.com/Azure/azure-mcp</a><br></p><h2>Days since Microsoft open source drama: 0</h2><p>The author of popular Kubernetes open source project <a href="https://github.com/spegel-org/spegel">Spegel</a> saw parts of their project copied by the Azure team to a project called <a href="https://github.com/Azure/peerd">Peerd</a>. Spegel is an OSS project released under the MIT license, so so what I hear you cry? Well the MIT license requires the copyrights of the author to remain in the source code and to be distributed with the software - the dev teams at Microsoft removed the LICENSE file from the &#8220;fork&#8221;, replacing it with their own, violating the license and therefore technically the copyright.</p><p>The blog post explains that MS &#8220;interviewed&#8221; the author before the &#8220;fork&#8221;, doing a brain dump from the author (probably recorded into OneNote and summarised by Copilot). A similar technique to the <a href="https://www.theverge.com/2020/6/2/21277863/microsoft-winget-windows-package-manager-appget-response-credit-comment">WinGet / AppGet</a> clone drama.</p><p>The Hacker News outrage has been brutal and in my opinion justified. I also put the word &#8220;fork&#8221; in quotes because in reality it looks like the project was copy and pasted, which isn&#8217;t technically a &#8220;fork&#8221;. <a href="https://news.ycombinator.com/item?id=43752389">This Hacker News sleuth figured it out.</a><br><br>Based on the commit history of Peerd essentially being a single person, I think that someone thought this was a slam dunk project for a promotion. Well, they&#8217;ve just set MS&#8217;s trust with the open source community back several years instead.<br><br><strong>The post</strong>: https://philiplaine.com/posts/getting-forked-by-microsoft/<br><strong>The Github meltdown</strong>: https://github.com/Azure/peerd/issues/109<br><strong>The damage control PR reply</strong>: https://news.ycombinator.com/item?id=43755745</p><div><hr></div><p>Thanks for reading as always! For a video version of this post, be sure to check out: </p><div id="youtube2-d4jUHwbffKQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;d4jUHwbffKQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/d4jUHwbffKQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.edandersen.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[.NET 10 Preview 3 releases and Semantic Kernel Python catches up to .NET]]></title><description><![CDATA[Ted's Tech, April 18th 2025]]></description><link>https://www.edandersen.com/p/net-10-preview-3-releases-and-semantic</link><guid isPermaLink="false">https://www.edandersen.com/p/net-10-preview-3-releases-and-semantic</guid><dc:creator><![CDATA[Ed Andersen]]></dc:creator><pubDate>Fri, 18 Apr 2025 23:33:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7gsb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Howdy,</p><p>This week has seen a couple of interesting emissions from the MS teams. I&#8217;ve read about them so you don&#8217;t have to. Thanks for remaining a newsletter subscriber as I try out this new format - let me know if it works for you.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7gsb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7gsb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 424w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 848w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1272w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7gsb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png" width="1133" height="684" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:684,&quot;width&quot;:1133,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:134619,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://edandersen.substack.com/i/161585803?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7gsb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 424w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 848w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1272w, https://substackcdn.com/image/fetch/$s_!7gsb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F846b4baa-71dc-498b-8aef-77d6238c2e3b_1133x684.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>.NET 10 Preview 3 lands</h2><p>Microsoft just <a href="https://devblogs.microsoft.com/dotnet/dotnet-10-preview-3/">dropped the third preview</a> of .NET 10, the latest sneak peek at the next Long-Term Support (LTS) version due later this year. LTS releases are very important as they are quite short lived (3 years) and every .NET 8 app will have only a year to update to .NET 10 before support stops, creating a bunch of activity. They&#8217;ve been busy&#8230;</p><p><strong>Highlights:</strong></p><ul><li><p><strong>C# 14:</strong> Just what we&#8217;ve always wanted (!) - extension methods, which let you add static methods, instance properties, and static properties to existing types like Ruby Monkey Patching from 2014. There's also a cleaner way to handle nulls with null-conditional assignment.</p></li><li><p><strong>ASP.NET Core &amp; Blazor:</strong> Blazor is still going (yeah), with some improvements to manage state. Blazor WebAssembly apps now get default HttpClient response streaming for better memory usage with large responses and support for fingerprinted static assets for better caching. Plus, built-in support for Server-Sent Events (SSE) is here for real-time updates - could this finally be the end of SignalR?</p></li><li><p><strong>.NET MAUI is still alive:</strong> MAUI devs will lament the loss of ListView, Cell and TableView as they are being <a href="https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview3/dotnetmaui.md">deprecated</a>. On the plus side, you get fullscreen video playback in Android WebViews and an easier way to check if location services are enabled with Geolocation.IsEnabled.</p></li></ul><p><strong>Helpful Links:</strong></p><ul><li><p><strong>.NET Blog Announcement:</strong> <a href="https://devblogs.microsoft.com/dotnet/dotnet-10-preview-3/">https://devblogs.microsoft.com/dotnet/dotnet-10-preview-3/</a></p></li><li><p><strong>InfoQ Article:</strong> <a href="https://www.infoq.com/news/2025/04/dotnet-10-preview3/">https://www.infoq.com/news/2025/04/dotnet-10-preview3/</a></p></li><li><p><strong>.NET 10 Release Notes (GitHub):</strong> <a href="https://github.com/dotnet/core/tree/main/release-notes/10.0">https://github.com/dotnet/core/tree/main/release-notes/10.0</a></p></li><li><p><strong>.NET Downloads:</strong> <a href="https://www.google.com/search?q=https://dotnet.microsoft.com/download/dotnet/10.0">https://dotnet.microsoft.com/download/dotnet/10.0</a></p></li></ul><h2>Semantic Kernel adds MCP and Google's A2A for Python users</h2><p>Microsoft's <a href="https://github.com/microsoft/semantic-kernel">Semantic Kernel</a> for Python just got a major upgrade, adding support for two important interoperability protocols: the <a href="https://modelcontextprotocol.io/introduction">Model Context Protocol (MCP)</a> and <a href="https://github.com/google/A2A">Google's Agent-to-Agent (A2A) protocol</a> - bringing it up to parity with the C# and .NET versions, which saw MCP capability added in March.</p><p>I&#8217;m normally super salty about this stuff, but seeing it come through first for .NET has been great, likely because they are using it for Copilot under the hood.</p><p><strong>What does this mean?</strong></p><ul><li><p><strong>MCP:</strong> SK can now act as both an <a href="https://devblogs.microsoft.com/semantic-kernel/semantic-kernel-adds-model-context-protocol-mcp-support-for-python/">MCP host/client and an MCP server</a>. Think of MCP like a "USB for AI" &#8211; it standardizes how AI apps connect to external tools and data. As a host, your SK agent can easily use tools exposed by <em>any</em> MCP server (like those for GitHub, Slack, or custom systems). As a server, you can share your SK functions, prompts, or even entire agents with other MCP clients, like Anthropic's Claude Desktop or potentially IDEs. This makes building complex, modular AI systems much easier if that is your kind of thing.</p></li><li><p><strong>A2A Integration:</strong> SK now also integrates with <a href="https://devblogs.microsoft.com/semantic-kernel/integrating-semantic-kernel-python-with-googles-a2a-protocol/">Google's A2A protocol</a> , which is designed for communication <em>between</em> different AI agents. The example MS have provided shows a &#8220;travel manager&#8221; agent delegating tasks (like currency conversion or activity planning) to specialized sub-agents using A2A for discovery and routing.</p></li></ul><p><strong>Sources:</strong></p><ul><li><p><strong>SK MCP Blog Post (Python):</strong> <a href="https://devblogs.microsoft.com/semantic-kernel/semantic-kernel-adds-model-context-protocol-mcp-support-for-python/">https://devblogs.microsoft.com/semantic-kernel/semantic-kernel-adds-model-context-protocol-mcp-support-for-python/</a></p></li><li><p><strong>SK A2A Blog Post (Python):</strong> <a href="https://devblogs.microsoft.com/semantic-kernel/integrating-semantic-kernel-python-with-googles-a2a-protocol/">https://devblogs.microsoft.com/semantic-kernel/integrating-semantic-kernel-python-with-googles-a2a-protocol/</a></p></li><li><p><strong>MCP Official Documentation:</strong> <a href="https://modelcontextprotocol.io/introduction">https://modelcontextprotocol.io/introduction</a></p></li><li><p><strong>Google A2A Repository (with SK sample):</strong> <a href="https://github.com/google/A2A">https://github.com/google/A2A</a></p></li><li><p><strong>SK Python MCP Samples:</strong> <a href="https://github.com/microsoft/semantic-kernel/tree/main/python/samples/concepts/mcp/">https://github.com/microsoft/semantic-kernel/tree/main/python/samples/concepts/mcp/</a></p></li><li><p><strong>SK Python MCP Demos:</strong> <a href="https://github.com/microsoft/semantic-kernel/tree/main/python/samples/demos/mcp_server/">https://github.com/microsoft/semantic-kernel/tree/main/python/samples/demos/mcp_server/</a></p></li></ul><h2>Copilot can now help with Azure SQL</h2><p><a href="https://techcommunity.microsoft.com/blog/azuresqlblog/announcing-general-availability-of-azure-sql-database-capabilities-for-microsoft/4403518">Microsoft have added a load of new Copilot functionality to the Azure Portal</a>, to basically save you a Google search or two.</p><p>You can now prompt it to help you&#8230;</p><ul><li><p><strong>Troubleshoot SQL Errors:</strong> Get help diagnosing common SQL errors (like 10928, 18456, 40613) with explanations and suggested fixes.</p></li><li><p><strong>Optimize Performance:</strong> Identify potential bottlenecks like storage or I/O limits and get recommendations.</p></li><li><p><strong>Simplify Configuration:</strong> Get guidance on choosing service tiers or constructing connection strings.</p></li><li><p><strong>Manage Security:</strong> Get assistance with Transparent Data Encryption (TDE) issues or data replication problems.</p></li></ul><p>Now originally they planned to allow you to use Copilot to directly write database queries in the Query editor within the Azure Portal, but it looks like they&#8217;ve decided to push this back and keep it exclusive to a future SSMS release. Seemed pretty useful.</p><p><strong>More info:</strong></p><ul><li><p><strong>Azure SQL Blog Announcement:</strong> <a href="https://techcommunity.microsoft.com/blog/azuresqlblog/announcing-general-availability-of-azure-sql-database-capabilities-for-microsoft/4403518">https://techcommunity.microsoft.com/blog/azuresqlblog/announcing-general-availability-of-azure-sql-database-capabilities-for-microsoft/4403518</a></p></li><li><p><strong>Microsoft Copilot in Azure Overview:</strong> <a href="https://learn.microsoft.com/en-us/azure/copilot/overview">https://learn.microsoft.com/en-us/azure/copilot/overview</a></p></li><li><p><strong>Copilot in Azure with Azure SQL DB:</strong> <a href="https://aka.ms/sqlcopilot">https://aka.ms/sqlcopilot</a></p></li><li><p><strong>FAQ:</strong> <a href="https://aka.ms/sqlcopilot-faq">https://aka.ms/sqlcopilot-faq</a></p></li><li><p><strong>Copilot in SSMS Session (SQL Conf 2025):</strong> <a href="https://learn.microsoft.com/en-us/shows/azure-developers-sql-conf-2025/a-developers-guide-to-using-copilot-in-ssms">https://learn.microsoft.com/en-us/shows/azure-developers-sql-conf-2025/a-developers-guide-to-using-copilot-in-ssms</a></p></li></ul><div><hr></div><p>Here is a video version of this post:</p><div id="youtube2-FzQlz_uzKQk" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;FzQlz_uzKQk&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/FzQlz_uzKQk?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><br><br>If there is anything you&#8217;d like to see me cover next week, send me a DM on Twitter/X at <a href="https://x.com/edandersen">@edandersen</a>. Cheers.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.edandersen.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>